% TOGAC44_AMBF_CartesianAdmittanceTest.m

% !!!!This script is best used when run in sections!!!!
% It is not reccommended to simply run this script as is. Initializatoin of
% the Galen_robot is a finicky process. Ensure you have the correct IP
% address or ROS initialization steps updated for your machine in the
% galen_interace.m file before running. HINT: running sections is easy.
% Instead of pressing the run section button in the editor, simply place
% and click your cursor in the section you want to run, then type cntrl +
% enter

% This script will execute the admittance controller for the open AMBF
% environment for the Galen Robot. 

% to run the system, run section 1 until you have successfully read the
% joint states and the force sensor data at the E35 joint
% if it fails to run or if you're waiting for the loop to continue because
% it's hung and subscribing or receiving then cancel the loop and try
% again. If this does not eventually work, then update your ROS_MASTER_URI
% or your ROS_IP in either the galen_interface file or on your linux
% machine. You may need to do a restart of your device.

% After running section 1
% control.
% After running section 1, you may run any of the section 2 commands to
% preemtively move the robot. If you don't want gravity on, don't forget to
% click in your AMBF window and press 2

% You can then run section 3 to control the robot

% Modified 05-1-2023
% Created by Brevin Banks

%% Section 1
galen_robot = galen_interface();  % initiate the robot interface

%%% Section 2
%% Section 2.1
galen_center_vel_based(galen_robot) % Home the robot
%% Section 2.2
uppose = single([.35,.35,.35,0,0,0]); % Home the galen robot partially above the midpoints
galen_center_vel_based(galen_robot,uppose)
%% Section 2.3
galen_robot.get_current_joints();  % center the galen robot rotational joints
current_position = galen_robot.extract_galen_pose();
uppose = single([current_position(1),current_position(2),current_position(3),0,0,0]);
galen_center_vel_based(galen_robot,uppose)

%% Section 3
% Assign A Step Force Input With Admittanc Control
galen_robot.set_control_type('Velocity');  % setup velocity control

% Step Input
current_velocity = galen_robot.get_current_joints();
current_position = galen_robot.extract_galen_pose();


% initialize all data storing vectors. Start at zero
Vref = [0;0;0;0;0;0];
Vref_prev = [0;0;0;0;0;0];
Vref_prev_prev = [0;0;0;0;0;0];
Vout = [0;0;0;0;0;0];
Vout_prev = [0;0;0;0;0;0];
Vout_prev_prev = [0;0;0;0;0;0];

% caputre time and iteration history
elapsed_time = [0];
k = 1;

% Initialize net and external forces
Fnet_prev = [0;0;0;0;0;0];
Fext = [0;0;0;0;0;0];
Fext_prev = [0;0;0;0;0;0];
Fext_prev_prev = [0;0;0;0;0;0];

% Initialize iteratior count, cv - output data of robot, rv - desired input
% for robot

i=1;
rext = [];
rf = [];
cv = [];
rv = [];
ri = [];
rqd = [];
cqd = [];
pout = [];

% Impedance From humnan h and envrionment e
Impede = true; % Boolean to apply impedance
mh = 0;
bh = 0;
kh = 401;
ke = 0;%16599;


% Initialize impedance force
Fh = [0;0;0;0;0;0];
Fhin = [0;0;0;0;0;0];
Fh_prev_prev = [0;0;0;0;0;0];
Fh_prev = [0;0;0;0;0;0];

% Unstable Admittance for Env contact, stable for human
% casestab = " (Unstable)";
% b_ad = [50;50;20;50;50;0];
% m_ad = [20;20;20;20;20;0];

% Desired Admittance values Stable
casestab = " (Stable)"; %stable if no simulated ke
b_ad = [150;170;170;170;270;0];
m_ad = [20;20;20;20;20;0];

% b_ad = [150;155;165;150;350;0];
% m_ad = [20;20;20;20;20;0];


% b_ad = [600;600;600;600;600;0];
% m_ad = [40;40;40;40;40;0];

% Initialize a vector to capture the change in time
time_vec = [];

desRate = 200;
des_t = 10;  %simulate for this many seconds
t_sig = 2.5; % Sigmoid rise time
traj = linspace(0,t_sig,t_sig*2*desRate);
traj2 = linspace(t_sig,des_t,(des_t-t_sig)*2*desRate);
traj_time = [traj traj2];
sig1 = 1./(1+exp(-traj*6+6));
sig2 = ones(1,length(traj2));
amplitude = -10;
sig = [sig1*amplitude sig2*amplitude];


r = rosrate(desRate);% rate control object will hold the loop at 200Hz
current_sim_time = 0; % initialize time at 0
reset(r) % clear any previously stored rate statistics


wc = 5; % Filter cutt off frequency
J = galen_robot.galen_jacob();
while(current_sim_time<des_t)
    %Grab the current sampling point in time relative to total time
    current_sim_time = r.TotalElapsedTime;
    elapsed_time(i+1) = r.TotalElapsedTime;
    Ts = elapsed_time(i+1)-elapsed_time(i);

    zext = traj_interp(current_sim_time,sig,traj_time);

    Fext = single([0.0,0.0, zext,0.0,0.0,0.0]'); % Step input reference 


    % Find net force, Input-impedance
    Fnet = Fext-Fh;

    % Record Velocity in history
    Vref_prev_prev = Vref_prev;
    Vref_prev = Vref;

    % Admittance controller tustin transform application
    for j=1:5
        Vref(j) = (-Vref_prev(j)*(b_ad(j)-((2/Ts)*m_ad(j))) + Fnet(j) + Fnet_prev(j))/(b_ad(j) + (2/Ts)*m_ad(j));
    end
    Vref(6)=0;
    qdot_ref = J\Vref;
    qdot_ref = single([qdot_ref(1);qdot_ref(2);qdot_ref(3);qdot_ref(4);0;qdot_ref(5)]);



    % Record Velocity out and force history
    Vout_prev_prev = Vout_prev;
    Vout_prev = Vout;

    Fnet_prev = Fnet;

    Fext_prev_prev = Fext_prev;
    Fext_prev = Fext;

    % Move the galen robot
    galen_robot.move_galen(qdot_ref);

    % Retrieve the current robot joints
    qdot_out = galen_robot.get_current_joints();
    J = galen_robot.galen_jacob();

    qdot_out =[qdot_out(1);qdot_out(2);qdot_out(3);qdot_out(4);qdot_out(6)];
    current_velocity = J*qdot_out;
    Vout = current_velocity;

    Fh_prev_prev = Fh_prev;
    Fh_prev = Fh;

    % Apply impedance if desired
    if Impede ==true
        % Impedance response Only takes in worst case scenario (Fe = ke + kh)
        for j=1:5
            Fh(j) = Vout(j)*((2*mh/Ts)+bh+((kh+ke)*Ts/2))+Vout_prev(j)*((-4*mh/Ts)+(Ts*(kh+kh)))+Vout_prev_prev(j)*((2*mh/Ts)-bh+((kh+ke)*Ts/2))+Fh_prev_prev(j);

        end
        Fh(6)= 0;
    else
        Fh = [0;0;0;0;0;0];
    end

    % Find the error between desired and output
    error = norm(current_velocity-qdot_ref);

    %Store input and output history
    rext(:,i) = Fext;
    rf(:,i) = Fnet;
    ri(:,i) = Fh;
    cv(:,i) = current_velocity;
    rv(:,i) = Vref;
    rqd(:,i) = qdot_ref;
    cqd(:,i) = qdot_out;
    pout(:,i) = galen_robot.extract_galen_pose();
    % Increase iterator and record current time
    i = i+1;
    time_vec(i) = current_sim_time;

    % Hold the loop if the sampling time is under 200Hz
    waitfor(r);
end

% Output the resulting total loop time and quality of the sampling rate
stats = statistics(r)
disp(r)
time_elapsed = r.TotalElapsedTime;



%% Section 4
% Plot the current values store in cv and rv. This should theoretically be
% the 3 different carriage joint values, and they should all be nearly
% identical if not for noise between sampling times and robot model
% innacuracies

t = time_vec(1:end-1); % not true seconds, Just a count for every existing sample.
% Update t above to be the true total loop time for the collected sample to
% get an accurate figure for the repsonse time

rv(1:3,:) = rv(1:3,:)*1000; % convert m to mm
cv(1:3,:) = cv(1:3,:)*1000; % convert m to mm

% plot all the cartesian velocities
close all
figure(1)
subplot(5,1,1)
hold on
plot(t,rv(1,:))
plot(t,cv(1,:))
legend('X Vref','X Vout')
ylabel('mm/s')

subplot(5,1,2)
hold on
plot(t,rv(2,:))
plot(t,cv(2,:))
legend('Y Vref','Y Vout')
ylabel('mm/s')

subplot(5,1,3)
hold on
plot(t,rv(3,:))
plot(t,cv(3,:))
legend('Z Vref','Z Vout')
ylabel('mm/s')

subplot(5,1,4)
hold on
plot(t,rv(4,:))
plot(t,cv(4,:))
legend('wx Vref','wx Vout')
ylabel('rad/s')

subplot(5,1,5)
hold on
plot(t,rv(5,:))
plot(t,cv(5,:))
legend('wy Vref','wy Vout')
xlabel('Time (s)')
ylabel('rad/s')


% plot the joint velocities

figure(2)
subplot(5,1,1)
hold on
plot(t,rqd(1,:))
plot(t,cqd(1,:))
legend('Car1 Vref','Car1 Vout')
ylabel('mm/s')

subplot(5,1,2)
hold on
plot(t,rqd(2,:))
plot(t,cqd(2,:))
legend('Car2 Vref','Car2 Vout')
ylabel('mm/s')

subplot(5,1,3)
hold on
plot(t,rqd(3,:))
plot(t,cqd(3,:))
legend('Car3 Vref','Car3 Vout')
ylabel('mm/s')

subplot(5,1,4)
hold on
plot(t,rqd(4,:))
plot(t,cqd(4,:))
legend('Roll Vref','Roll Vout')
ylabel('rad/s')

subplot(5,1,5)
hold on
plot(t,rqd(6,:))
plot(t,cqd(5,:))
legend('Tilt Vref','Tilt Vout')
xlabel('Time (s)')
ylabel('rad/s')

% plot the input force, impedance, and net force
figure(3)
for j=1:5
    subplot(5,1,j)
    hold on
    plot(elapsed_time(1:end-1),rext(j,:),'m')
    plot(elapsed_time(1:end-1),rf(j,:),'g')
    plot(elapsed_time(1:end-1),ri(j,:),'k')
    legend('Input Force: Fext','Net Force','Equiv Impedance')
    if j<4
        ylabel('F N')
    else
        ylabel('T Nm')
    end
    xlabel('Time s')
    madstring = string(m_ad(j));
    badstring = string(b_ad(j));
    switch(j)
        case 1
            ftyp = "X";

        case 2
            ftyp = "Y";

        case 3
            ftyp = "Z";

        case 4
            ftyp = "Wx";

        case 5
            ftyp = "Wy";

    end
    title(ftyp +" Interaction Forces with des m_{ad} = " + madstring +" des b_{ad} = " + badstring + casestab)
    if j==1
        subtitle('Sigmoid trajectory force of -10 N with Sim Object Contact and Worst Case Arm Impedance')
    end
end
mpout = pout*1000;

% Plot the output joint positions
figure(4)

subplot(5,1,1)
hold on
plot(t,mpout(1,:))
legend('Car1 Pout')
ylabel('mm/s')
title('Current Joint States')

subplot(5,1,2)
hold on
plot(t,mpout(2,:))
legend('Car2 Pout')
ylabel('mm/s')

subplot(5,1,3)
hold on
plot(t,mpout(3,:))
legend('Car3 Pout')
ylabel('mm/s')

subplot(5,1,4)
hold on
plot(t,pout(4,:))
legend('Roll Pout')
ylabel('rad/s')

subplot(5,1,5)
hold on
plot(t,pout(6,:))
legend('Tilt Pout')
xlabel('Time (s)')
ylabel('rad/s')

CartPout = [];
% convert the joint positions to cartesian positions
for k=1:length(pout(1,:))
    pfk = [pout(1:4,k);(pout(6,k))];
    TF = galen_robot.galen_fk(pfk);
    Eulout = rotm2eul(TF(1:3,1:3),'ZYX');
    Rx(k) = Eulout(3);
    Ry(k) = Eulout(2);
    Rz(k) = Eulout(1);
    CartPout(:,k) = [TF(1:3,4);Ry(k);Rx(k)];
end


CartPout(1:3,:) = CartPout(1:3,:)*1000;

% Plot the ouptut cartesian positions
figure(5)
subplot(5,1,1)
hold on
plot(t,CartPout(1,:))
legend('X Pout')
ylabel('mm/s')
title('Position of End effector relative to world frame')

subplot(5,1,2)
hold on
plot(t,CartPout(2,:))
legend('Y Pout')
ylabel('mm/s')

subplot(5,1,3)
hold on
plot(t,CartPout(3,:))
legend('Z Pout')
ylabel('mm/s')

subplot(5,1,4)
hold on
plot(t,CartPout(4,:))
legend('wx Pout')
ylabel('rad/s')

subplot(5,1,5)
hold on
plot(t,CartPout(5,:))
legend('wy Pout')
xlabel('Time (s)')
ylabel('rad/s')


%%




% Center the robot
% Will move the robot to the indicated pose in other_pose, or the home
% position if no input is given
% Input:
%   galen_robot - the robot object from galen_interface()
%   other_pose - desired centered pose
% Note: this function will not use any optimization of trajectories and
% will drive the galen hard towards its goal poses
function galen_center_vel_based(galen_robot,other_pose)

galen_robot.set_control_type('Velocity');  % setup velocity control
if nargin<2
    ref_pose = single([0.210,0.210,0.210,0.0,0.0,0.0]'); %Home pose
else
    ref_pose = other_pose'; %Home pose
end
galen_robot.get_current_joints();
current_position = galen_robot.extract_galen_pose();

% Initialize iteratior count, cv - output data of robot, rv - desired input
% for robot
i=1;
% Initialize a vector to capture the change in time



ref_velocity = single([0.0,0.0,0.0,0.0,0.0,0.0]');



r = rosrate(200);% rate control object will hold the loop at 200Hz

reset(r) % clear any previously stored rate statistics

sendout = false;

while(sendout==false || i<50)
    %Grab the current sampling point in time relative to total time


    for k = 1:6
        if k~=5
            if abs(current_position(k)-ref_pose(k))>0.002
                if current_position(k)>ref_pose(k)
                    ref_velocity(k) = ref_velocity(k)-single(0.002);
                elseif current_position(k)<ref_pose(k)
                    ref_velocity(k) = ref_velocity(k)+single(0.002);
                end
            else
                ref_velocity(k) = ref_velocity(k)/2;
            end
            if abs(ref_velocity(k))>0.05
                ref_velocity(k)= sign(ref_velocity(k))*0.05;
            end
        end
    end

    % Move the galen robot
    galen_robot.move_galen(ref_velocity);

    % Retrieve the current robot joints
    galen_robot.get_current_joints();
    current_position = galen_robot.extract_galen_pose();

    % Increase iterator and record current time
    i = i+1;

    norm(current_position-ref_pose);
    norm(ref_velocity);

    if norm(current_position-ref_pose)<0.003 && norm(ref_velocity)<0.003
        if sendout == false
            i = 0;
        end
        sendout = true;

    end

    % Hold the loop if the sampling time is under 200Hz
    waitfor(r);
    if i>7000
        break
    end

end

end