Skip to the content.

Capstone Project

Main script: capstone_main.m

%% This script produces csv files that will guide a youbot (4 mecanum whee
%  -ls chassis + UR5 arm) to perform pick-drop action. The script takes
%  four input, namely:
%  1. initial configuration of the end-effector in the form of
%     [chassis phi, x, y, J1, J2, J3, J4, J5, W1, W2, W3, W4]
%  2. location of cube-to-pick:
%     [x, y, theta]
%  3. goal location to drop the cube:
%     [x, y, theta]
%  4. Error gains:
%     [Kp, Ki]
%=========================================================================%
%  For more information about this assignment, please visit:
%  http://hades.mech.northwestern.edu/index.php/Mobile_Manipulation_Capstone
%=========================================================================%
%  Written by Donglin Sui (lordblackwoods@gmail.com) on 2020/04/03
%=========================================================================%
%                     The Program Begins Here.
%                         _.,------..
%                        /     `"_
%                       ノ        \
%                      ノ          \
%                      |  /\  /\    |
%                      |          |
%                      \  (_人_)    /
%                    ....—\       _/———.
%                   |"    ' "_    ..,-' '|
%                   ' ̄ ̄ ̄\       / ̄ ̄ ̄'
%                        |       |
%                        \   |' ̄\` |
%                        \   \   /
%                         \  \__/
%                          \__、\
%=========================================================================%


%--------------------------------------------------------------------------
%                              Constants
%--------------------------------------------------------------------------
%% Kinematic Model of the youBot
%  For detail information, please consult #Kinematics of the youBot section
%  of the page: 
%  http://hades.mech.northwestern.edu/index.php/Mobile_Manipulation_Capstone
% Fixed offset from chassis frame {b} to base frame of arm {0}: Tb0
T_b0 = [1 0 0 0.1662;
        0 1 0    0  ;
        0 0 1 0.0026;
        0 0 0    1  ;];
    
% When arm at home config, end-effector frame {e} relative to arm base 
%  frame {0}, i.e., the home transformation matrix M0e is
M_0e = [1 0 0 0.033 ;
        0 1 0   0   ;
        0 0 1 0.6546;
        0 0 0   1   ;];
    
% Body screw axes list, Blist
B1 = [0 0 1 0 0.033 0]';
B2 = [0 -1 0 -0.5076 0 0]';
B3 = [0 -1 0 -0.3526 0 0]';
B4 = [0 -1 0 -0.2176 0 0]';
B5 = [0 0 1 0 0 0]';
BList = [B1 B2 B3 B4 B5];

% Chassis model
l = 0.47/2;
w = 0.3/2;
r = 0.0475;

% create moRobo Object
myRobo = youbotKUKA(l, w, r, 'KUKA youbot with UR5 end-effector', ...
                    T_b0, M_0e, BList);
fprintf('Successfully created the youbot kinematic model!\n')
disp('myRobo')

%--------------------------------------------------------------------------
%                               User Input
%--------------------------------------------------------------------------
%% Asking for input
fprintf('***=========================================================================***\n')
fprintf('Please fill in the following initial variables so as to perform the simulation.\n')

% initial configuration of end-effector initial_T_se
[initial_T_se, initial_Config] = getInitialConfiguration(M_0e,BList,T_b0);

% initial and final cube location, initial_T_sc & goal_T_sc
initial_T_sc = getInitialCubeLocation();

goal_T_sc = getGoalCubeLocation();

% compute standoff and gripping HTM
T_ce_standoff = getCEStandoffHTM();
T_ce_gripping = getCEGrippingHTM();

% reference initial configuration to test the feedback control
% because input a matrix in Command Window is troublesome, this value is
% hard-coded here
ref_initial_T_se = [ 0 0 1   0;
                     0 1 0   0;
                    -1 0 0 0.5;
                     0 0 0   1];
ini_Err = HTM2pose(ref_initial_T_se) - HTM2pose(initial_T_se);

% feedback gains
gains = getControlGains();

%% Reference end-effector configuration
initial_T_standoff = initial_T_sc * T_ce_standoff;
T_grasp = initial_T_sc * T_ce_gripping;
goal_T_standoff = goal_T_sc * T_ce_standoff;
T_release = goal_T_sc * T_ce_gripping;

% Arranged in a way so that it can be looped in the 'Steps' section
Configurations = {ref_initial_T_se, initial_T_standoff, T_grasp, T_grasp,...
                  initial_T_standoff, goal_T_standoff, T_release, T_release...
                  goal_T_standoff};
    
%--------------------------------------------------------------------------
%                      Generate reference trajectory
%--------------------------------------------------------------------------
%% Generate reference trajectory
delt_t = 0.01;      % 10ms interval
motionTime = 13;    % total motion time
Tf = computeTf(motionTime, Configurations);

% 8 Steps
% 1. A trajectory to move the gripper from its initial configuration to
%    a "standoff" configuration a few cm above the block.
% 2. A trajectory to move the gripper down to the grasp position.
% 3. Closing of the gripper.
% 4. A trajectory to move the gripper back up to the "standoff" 
%    configuration.
% 5. A trajectory to move the gripper to a "standoff" configuration 
%    above the final configuration.
% 6. A trajectory to move the gripper to the final configuration of the 
%    object.
% 7. Opening of the gripper.
% 8. A trajectory to move the gripper back to the "standoff" 
%    configuration.

grpOPEN = 1;
grpCLOSE = 0;
methodOrder = 5;    % quintic polynomials
gripperStatus = 0;

% trajectory in form of 
% [r11 r12 r13 r21 r22 r23 r31 r32 r33 Xpos Ypos Zpos gripperStatus]
traj = []; 

for ii = 1 : 8
    if ii == 3
        % gripperStatus is a BOOL-type variable, in which
        % 1 = gripper closed
        % 0 = gripper opened
        gripperStatus = grpOPEN;  
    elseif ii == 7
        gripperStatus = grpCLOSE;
    end
    temp_traj = myRobo.trajectoryGenerator(Configurations{ii}, ...
                                           Configurations{ii+1}, ...
                                           Tf(ii), ...
                                           delt_t, ...
                                           gripperStatus, ...
                                           methodOrder);
    traj = [traj; temp_traj];
    % the above trajectory of the end-effector is used later to guide the
    % motion of the KUKA youbot
end

%--------------------------------------------------------------------------
%                           Feedback PI controller
%--------------------------------------------------------------------------
%% PI Feedback control
myRobo.q = [initial_Config(1) initial_Config(2) initial_Config(3)];

kp = gains(1);
myRobo.Kp = kp * eye(6);
ki = gains(2);
myRobo.Ki = ki * eye(6);

myRobo.theta = [0 0 0.2 -1.67 0]';
myRobo.wheelAngle = [0 0 0 0];

speedLimit = 12.3 * ones(1,9);
jointLimit = [ones(1,5) * pi; ones(1,5) * (-pi)];

[NHTM, NgripperStatus] = NTraj2NHTM(traj);

[scene6simulation, Xerr] = myRobo.feedbackController(delt_t,NHTM,...
                                                     speedLimit,jointLimit,...
                                                     NgripperStatus);
writematrix(scene6simulation,'scene6simulation.csv','Delimiter','comma');

plot(Xerr','LineWidth',1.5);
title('$X_{err}$ against time','FontSize',12,'FontWeight','bold',...
      'interpreter', 'latex')
xlabel('Time (10ms)','FontSize',12,'FontWeight','bold');
ylabel('Error Twist','FontSize',12,'FontWeight','bold');
legend('$W_x$','$W_y$','$W_z$','$V_x$','$V_y$','$V_z$',...
       'interpreter','latex')
grid on;
writematrix(Xerr,'Xerr.csv','Delimiter','comma');
 
            
%--------------------------------------------------------------------------
%                       Functions are defined below
%--------------------------------------------------------------------------
%% getInitialConfiguration
%  ask the user to input the initial configuration of the robot in array
%  form and then compute based on the input the HTM of the initial 
%  configuration of end-effector: ini_T_se
%  For testing, type:
%  [0.6, 0.0, 0.0, 0.0, -0.35, -0.698, -0.505, 0.0, 0.0, 0.0, 0.0, 0.0]
function [ini_T_se, iniConfig] = getInitialConfiguration(M_0e,BList,T_b0)
    prompt1 = ['\n 1. Please enter the initial configuration, Tse, of the\n'...
              'end-effector in the form of\n'...
              '[phi, x, y, J1, J2, J3, J4, J5, W1, W2, W3, W4] :\n'];
    iniConfig = input(prompt1);

    % generate transformation matrix for the end-effector from input config
    R_vec = [0 0 iniConfig(1)];
    R_so3 = VecToso3(R_vec);
    R = MatrixExp3(R_so3);
    p = [iniConfig(2) iniConfig(3) 0.0963]';
    T_sb = RpToTrans(R,p);
    T_0e = FKinBody(M_0e, BList, iniConfig(4:8)');
    T_be = T_b0 * T_0e;
    ini_T_se = T_sb * T_be;
end

%% getInitialCubeLocation
%  ask the user to input the initial location of the cube-to-pick in array
%  form and then compute based on the input the HTM of the cube
%  configuration.
%  For testing, type [1, 0, 0]
function ini_T_sc = getInitialCubeLocation()
    prompt2 = ['\n 2. Please enter the initial location of the cube-to-pick in the\n'...
               'form of [x, y, theta] : \n'];
    temp = input(prompt2);
    x = temp(1);
    y = temp(2);
    theta = temp(3);
    rot = [0 0 theta]';
    pos = [x y 0.025]';
    R_so3 = VecToso3(rot);
    R = MatrixExp3(R_so3);
    ini_T_sc = RpToTrans(R,pos);
end

%% getGoalCubeLocation
%  ask the user to input the initial location of the cube-to-drop in array
%  form and then compute based on the input the HTM of the cube
%  configuration
%  For testing, type [0, -1, -pi/2]
function goal_T_sc = getGoalCubeLocation()
    prompt3 = ['\n 3. Please enter the goal location of the cube-to-drop in the\n'...
               'form of [x, y, theta] : \n'];
    temp = input(prompt3);
    x = temp(1);
    y = temp(2);
    theta = temp(3);
    rot = [0 0 theta]';
    pos = [x y 0.025]';
    R_so3 = VecToso3(rot);
    R = MatrixExp3(R_so3);
    goal_T_sc = RpToTrans(R,pos);
end

%% getControlGains
%  ask the user to input the feedback controller's gains, namely the ki and
%  kp
function gains = getControlGains()
    prompt4 = ['\n 4. Please enter the feedback controller gains in\n'...
               'the form of [kp, ki] : \n'];
    gains = input(prompt4);
end

%% getCEStandoffHTM
%  get standoff HTM
function T_ce_standoff = getCEStandoffHTM()
    theta = [0 pi/5+pi/2 0]';
    R_so3 = VecToso3(theta);
    R = MatrixExp3(R_so3);
    pos = [0 0 0.250]';
    T_ce_standoff = RpToTrans(R,pos);
end

%% getCEGrippingHTM
%  get gripping HTM
function T_ce_gripping = getCEGrippingHTM()
    theta = [0 pi/5+pi/2 0]';
    R_so3 = VecToso3(theta);
    R = MatrixExp3(R_so3);
    pos = [0 0 0]';
    T_ce_gripping = RpToTrans(R,pos);
end

%% Convert a HTM to pose ([Rx Ry Rz x y z]')
function pose = HTM2pose(HTM)
    [R, p] = TransToRp(HTM);
    R_so3 = MatrixLog3(R);
    rot = so3ToVec(R_so3);
    pose = [rot; p];
end

%% Convert a traj to HTM
%  convert a traj in form of
%  N * [r11 r12 r13 r21 r22 r23 r31 r32 r33 Xpos Ypos Zpos gripperStatus]
%  into a 3x3xN tensor containing the HTM and a 1xN array containing the
%  gripperStatus
function [NHTM, NgripperStatus] = NTraj2NHTM(Ntraj)
    N = size(Ntraj,1);
    NHTM = zeros(4,4,N);
    NgripperStatus = zeros(1,N);
    for ii = 1 : N
        r1j = Ntraj(ii,1:3);
        r2j = Ntraj(ii,4:6);
        r3j = Ntraj(ii,7:9);
        R = [r1j; r2j; r3j];
        p = Ntraj(ii,10:12)';
        temp_HTM = RpToTrans(R,p);
        NHTM(:,:,ii) = temp_HTM;
        NgripperStatus(ii) = Ntraj(ii,13);
    end
end

%% Compute weighted time Tf using Euclidean distance
function Tf = computeTf(motionTime, Configurations)
    % Initialize variables
    T_se_initial = Configurations{1};
    T_standoff_initial = Configurations{2};
    T_grasp = Configurations{3};
    T_standoff_final = Configurations{6};
    T_release = Configurations{7};

    % for debugging
    %fprintf('T_se_initial:\n')
    %disp(T_se_initial)
    %fprintf('T_standoff_initial:\n')
    %disp(T_standoff_initial)
    %fprintf('T_grasp:\n')
    %disp(T_grasp)
    %fprintf('T_standoff_final:\n')
    %disp(T_standoff_final)
    %fprintf('T_release:\n')
    %disp(T_release)

    normalized_D = [norm(T_se_initial(1:3,4)-T_standoff_initial(1:3,4)),...
                    norm(T_standoff_initial(1:3,4)-T_grasp(1:3,4)),...
                    0,...
                    norm(T_standoff_initial(1:3,4)-T_grasp(1:3,4)),...
                    norm(T_standoff_initial(1:3,4)-T_standoff_final(1:3,4)),...
                    norm(T_standoff_final(1:3,4)-T_release(1:3,4)),...
                    0,...
                    norm(T_standoff_final(1:3,4)-T_release(1:3,4))];
    D_sum = sum(normalized_D);

    % 8 Steps
    % 1. A trajectory to move the gripper from its initial configuration to
    %    a "standoff" configuration a few cm above the block.
    % 2. A trajectory to move the gripper down to the grasp position.
    % 3. Closing of the gripper.
    % 4. A trajectory to move the gripper back up to the "standoff" 
    %    configuration.
    % 5. A trajectory to move the gripper to a "standoff" configuration 
    %    above the final configuration.
    % 6. A trajectory to move the gripper to the final configuration of the 
    %    object.
    % 7. Opening of the gripper.
    % 8. A trajectory to move the gripper back to the "standoff" 
    %    configuration.
    
    % N.B. that each of step 3 & 7 should consist of at least 63 identical 
    % lines of the csv file in which the gripper state matches previous 
    % line in the csv file, so as to make sure that the gripper motion is 
    % completed.
    
    t = zeros(1,8);
    gripperRunningTime = 0.63;      % 630 ms, 63 of 10ms intervals
    for i = 1:8
        if (i == 3) || (i == 7)
            t(i) = gripperRunningTime;            
        else
            t(i) = normalized_D(i) * (motionTime - 2 * gripperRunningTime) / ...
                 D_sum;
        end
    end

    Tf = round(t * 10^2) / 10^2;    % round into multiples of 10ms 
end

superclass: wheeledMobileRobot.m

%% This file defines a super class called 'wheeledMobileRobot'.
%  This file is part of the capstone project of the Coursera MOOC Modern
%  Robotics Course 6. 
%  Written by Donglin Sui (lordblackwoods@gmail.com) on 2020/04/05
%  In MATLAB, the relation between a subclass and a super class is like
%  the relation between 'Desk' and 'Furniture', in which the later contains
%  the generic information of certain object, and the former contains
%  (usually) more specific pieces of information. For readers who are not
%  familiar with OOP (Object-Oriented-Programming), check this Q&A :
%  https://www.mathworks.com/matlabcentral/answers/89957-why-subclass-superclass-in-oop
classdef wheeledMobileRobot
    properties
        % kinematic model
        l           % length from center of car to wheel axis in x_b 
                    % direction, the half_length
        w           % length from center of car to wheel in y_b direction, 
                    % the half_width
        r           % radius of wheel
        type        % robot type, in this case, the type is 'youBot'
        
        % for feedback PI control
        q           % in the form of [chassis_phi x y]
        q_dot
        Vb
        u
        
        % odometry
        wheelAngle
    end
    
    properties (Constant)
        height = 0.0963 
    end
    
    properties (Dependent)
        config
        H_0
    end
    
    methods
        function obj = set.u(obj,val)
             obj.u = val;
        end
        
        % The following three functions are used for the dependent
        % properties
        function temp = get.config(obj)
           theta = obj.q(1);
           R = [cos(theta) -sin(theta) 0;
                sin(theta)  cos(theta) 0;
                    0           0      1];
           x = obj.q(2);
           y = obj.q(3);
           z = obj.height;
           p = [x y z]';
           temp = RpToTrans(R,p);
        end
        
        function temp = get.H_0(obj)
            rr = obj.r;
            ll = obj.l;
            ww = obj.w;
            temp = [-(ll+ww)/rr 1/rr -1/rr;
                     (ll+ww)/rr 1/rr  1/rr;
                     (ll+ww)/rr 1/rr -1/rr;
                    -(ll+ww)/rr 1/rr  1/rr;];
        end
        
        % This function allows the creation of kinematic model of chassis
        function obj = wheeledMobileRobot(half_length,half_width,...
                                          radius,roboType)
            obj.l = half_length;
            obj.w = half_width;
            obj.r = radius;
            obj.type = roboType;
        end
        
        function obj = nextState(obj, u, delt_t, speedLimit)
            u(find((u>=speedLimit) == 1)) = 12.3;
            
            % Odometry
            % Vb = H(0)^{dagger} * delta_theta = F * delta_theta
            delta_theta = u';
            obj.Vb = pinv(obj.H_0) * delta_theta;
            
            % chassis twist
            omega_bz = obj.Vb(1);
            v_bx = obj.Vb(2);
            v_by = obj.Vb(3);
            % 6-dimensional version of the planar chassis twist 
            vb6 = [0 0 omega_bz v_bx v_by 0]';
            % integrate vb6 over delt_t so as to generate the displacement
            % created by the wheelAngle increment
            vb6_integral = vb6 * delt_t;
            vb6_se3mat = VecTose3(vb6_integral);
            T_bb_dash_SE3 = MatrixExp6(vb6_se3mat);
            T_k = obj.config;
            T_k_next = T_k * T_bb_dash_SE3;
            
            [R, p] = TransToRp(T_k_next);
            R_so3 = MatrixLog3(R);
            rot = so3ToVec(R_so3);
            chassis_phi_next = rot(3);
            x_next = p(1);
            y_next = p(2);
            
            obj.q = [chassis_phi_next x_next y_next];
            obj.wheelAngle = obj.wheelAngle + u*delt_t;
            
                
            %T_bk_bknext = MatrixExp6(VecTose3(vb6*delt_t));
            %T_s_bknext = obj.config*T_bk_bknext;

            %obj.q = [atan2(T_s_bknext(2,1), T_s_bknext(1,1)),T_s_bknext(1,4),T_s_bknext(2,4)];
            %obj.wheelAngle = obj.wheelAngle + u*delt_t;
            
       end
        
    end
    
    
end

subclass: youbotKUKA.m

%% Subclass of 'wheeledMobileRobot'
classdef youbotKUKA < wheeledMobileRobot
    properties
        % for kinematic model
        M_be
        Blist
        
        % trajectory
        Trajectory
        
        % for feedback PI control
        theta
        theta_dot
        Kp
        Ki
        
        % for forward kinematic
        T_be
        T_se
    end
    
    properties (Dependent)
        Je
    end
    
    methods
        function Je = get.Je(obj)
            J_arm = JacobianBody(obj.Blist,obj.theta);
            F3 = pinv(obj.H_0, 0.0001);
            F6 = zeros(6,4);
            F6(3:5,:) = F3;
            T_eb = TransInv(obj.T_be);
            Ad_T_eb = Adjoint(T_eb);
            J_base = Ad_T_eb * F6;
            Je = [J_base J_arm];
        end
        
        function obj = youbotKUKA(half_length, half_width, radius,...
                                  roboType, T_b0, M_0e, BList)
            obj@wheeledMobileRobot(half_length, half_width, radius, ...
                                   roboType);
            obj.M_be = T_b0 * M_0e;
            obj.Blist = BList;
        end
        
        % This function shuffles a HTM into array form
        % [r11 r12 r13 r21 r22 r23 r31 r32 r33 Xpos Ypos Zpos gripperStatus]
        function formattedTraj = shuffleTrajectory(obj,gripperStatus)
            len = size(obj.Trajectory,2);
            formattedTraj = zeros(len,13);
            for ii = 1 : len
                temp_HTM = obj.Trajectory{ii};
                r1j = temp_HTM(1,1:3);
                r2j = temp_HTM(2,1:3);
                r3j = temp_HTM(3,1:3);
                p = temp_HTM(1:3,4)';
                formattedTraj(ii,:) = [r1j, r2j, r3j, p, gripperStatus];
            end
        end
        
        function traj = trajectoryGenerator(obj, Xstart, Xend, T, delt_t,...
                                            gripperStatus, methodOrder)
            N = round(T/delt_t + 1);
            obj.Trajectory = CartesianTrajectory(Xstart, Xend, T, N, ...
                                                 methodOrder);
            % shuffle the trajectory into the output format, which is 
            % [r11 r12 r13 r21 r22 r23 r31 r32 r33 Xpos Ypos Zpos gripperStatus]
            %  rij = terms in the rotation section of the HTM
            %  Xpos = x-coordinate in the p section of the Rp HTM
            %  gripperStatus is a boolean value defined as true = closed, false = open
            formattedTraj = shuffleTrajectory(obj,gripperStatus);
            traj = round(formattedTraj, 14);    % this rounding digit is 
                                                % chosen arbitrarily and 
                                                % can be reduced to 8
        end
        
        % forward kinematics
        function obj = FK(obj)
            temp = FKinBody(obj.M_be, obj.Blist, obj.theta);
            obj.T_be = temp;
            obj.T_se = obj.config * temp;
        end
        
        function obj = nextState(obj,config,speed,delt_t,speedLimit)
            obj = nextState@wheeledMobileRobot(obj,speed(1:4),delt_t,speedLimit(1:4));
            % using logical matrix to index which element the speed is
            % bigger than speedLimit
            speed = speed(5:9);
            speedLimit = speedLimit(5:9);
            speed(find((speed>=speedLimit)==1)) = 12.3;
            obj.theta = config(4:8) + speed * delt_t; 
            obj.theta = obj.theta';
       end
        
        function [scene6simulation, Xerr, obj] = feedbackController(obj,...
                                                 delt_t,NHTM,...
                                                 speedLimit,jointLimit,...
                                                 NgripperStatus)
            N = size(NHTM,3);
            scene6simulation = zeros(N,13);
            Xerr = zeros(6,N-1);
            V = zeros(6,N-1);
            for ii = 1 : (N-1)
                obj = obj.FK;
                T_se_temp = obj.T_se;
                obj.T_be = FKinBody(obj.M_be, obj.Blist, obj.theta);
                
                % initialize terms required by eqn. (13.37) of the textbook
                Xd = NHTM(:,:,ii);
                Xd_inv = TransInv(Xd);
                Xd_next = NHTM(:,:,ii+1);
                Td_next = Xd_inv * Xd_next;
                Vd_se3 = MatrixLog6(Td_next);
                Vd_se3 = Vd_se3 / delt_t;
                Vd = se3ToVec(Vd_se3);
                T_es_temp = TransInv(T_se_temp);
                T_err = T_es_temp * Xd;
                xerr_se3 = MatrixLog6(T_err);
                xerr = se3ToVec(xerr_se3);
                Xerr(:,ii) = xerr;
                XerrSum = sum(Xerr,2); 
                Ad_Terr = Adjoint(T_err);
                
                % now apply eqn. (13.37)
                V(:,ii) = Ad_Terr * Vd + obj.Kp * xerr + obj.Ki * XerrSum ...
                                                                  * delt_t;
                % the commanded end-effector-frame twist is thus
                % implemented as commanded_Twist = J^{dagger}_{e} * V
                commanded_Twist = pinv(obj.Je, 0.0090) * V(:,ii);
                obj.u = commanded_Twist(1:4);
                obj.theta_dot = commanded_Twist(5:9);
                commanded_Twist = [obj.u' obj.theta_dot'];
                config = [obj.q obj.theta' obj.wheelAngle];
                
                % Joint limit test
                Je_new = jointLimitCheck(obj, jointLimit, commanded_Twist, ...
                                         config, delt_t, speedLimit);
                commanded_Twist = pinv(Je_new,0.0090) * V(:,ii);
                obj.u = commanded_Twist(1:4);
                obj.theta_dot = commanded_Twist(5:9);
                commanded_Twist = [obj.u' obj.theta_dot'];
                
                obj = obj.nextState(config, commanded_Twist, delt_t, ...
                                    speedLimit); 
                scene6simulation(ii+1,:) = [obj.q ...
                                            obj.theta' ...
                                            obj.wheelAngle ...
                                            NgripperStatus(ii)];
            end
        end
        
        function Je = jointLimitCheck(obj, jointLimit, speed, config, ...
                                      delt_t, speedLimit)
           obj_test = obj.nextState(config,speed,delt_t,speedLimit);
           No_joint = size(jointLimit,2);
           Je = obj.Je;
           theta_test = obj_test.theta';
           for i = 1 : No_joint
               if theta_test(i) > jointLimit(1,i)||theta_test(i) < jointLimit(2,i)
                   Je(:,i+4) = zeros(6,1);
               end   
           end
        end
        
    end
    
end