% TOGAC43_AMBF_VelocityController.m

% This file will run basic control inputs on the galen robot for the ambf
% simulation. This contects matlab with ambf using the galen_interface
% class. Ensure ROS is running by running roscore in a terminal and then
% open the ambf model in a separate terminal
% Instructions on how to install the AMBF application and how to use the
% robot with ambf are found in:

% TOGAC25_AMBF_IN_AMBFforGalen.pdf

% Instructions for using matalb with ambf are found in:

% TOGAC29_AMBF_IN_ConnectMatlab2AMBF.pdf

% 3 main control types are avaliable. Chirp, Linear Trajectory, Sine, and Step inputs can be
% used. After the control loop sections, you can plot rv and cv, the output
% results.

% !!!!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

% 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

% Created by Brevin Banks
% Modified 4/29/2023


%% 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 Chirp Input
galen_robot.set_control_type('Velocity');  % setup velocity control

% Initialize the error for iteration
error = 1;

%Generate Chirp
des_sim_time = 60;
sim_time_window = (0:1/200:des_sim_time); % 50 second window with 200Hz sampling
f0 =   0.001; % Starting frequency 0.001 Hz
f1 = 5; % ending frequency 5 Hz
Fs = 1/mean(diff(sim_time_window));
x = chirp(sim_time_window,f0,sim_time_window(end),f1,'linear',-90); % generate chirp magnitude 0.05

% Initialize iteratior count, cv - output data of robot, rv - desired input
% for robot
i=1;
cv = [];
rv = [];
rqd = [];
cqd = [];
% Initialize a vector to capture the change in time
time_vec = [];

r = rosrate(200);% 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

J = galen_robot.galen_jacob();
while(current_sim_time<des_sim_time+1) % for ~50 seconds run the loop
    %Grab the current sampling point in time relative to total time
    current_sim_time = r.TotalElapsedTime;

    % find the closest point to time in the chirp function
    [ d, ix ] = min( abs( sim_time_window-current_sim_time) );
    if ix <2
        ix =2; % can't interpolate starter position without more than 2 samples
    elseif ix>length(x)-1
        ix = length(x)-1; % can't interpolate end position without if more than 10000 samples
    end
    %interpolate to get the value of the chirp function
    Cinput = x(ix-1)+(current_sim_time-sim_time_window(ix-1))*(((x(ix+1) - x(ix-1))/(sim_time_window(ix+1) - sim_time_window(ix-1))));
    if current_sim_time>des_sim_time % can't interpolate at the end conditions. Just assign 0
        Cinput = 0;
    end

    % Assign the desired velocity for 3 carriage joints
    ref_velocity = single([0,0,Cinput*0.01,0,0,0]');

    qdot_ref = J\ref_velocity;
    qdot_ref = [qdot_ref(1);qdot_ref(2);qdot_ref(3);qdot_ref(4);0;qdot_ref(5)];
    


    % 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;
    % Find the error between desired and output
    error = norm(current_velocity-qdot_ref);

    %Store input and output history
    cv(:,i) = current_velocity;
    rv(:,i) = ref_velocity;
    rqd(:,i) = qdot_ref;
    cqd(:,i) = qdot_out;
    % 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;

% Plot the shape of the Velocity input response
figure(2)
plot(sim_time_window,x)
title('Chirp Input 0.001Hz to 5Hz for 60s')
xlabel('Time (s)')
ylabel('Velocity input (m/s)')


%% Assign A Step Input

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 iteratior count, cv - output data of robot, rv - desired input
% for robot
i=1;
cv = [];
rv = [];
rqd = [];
cqd = [];
% Initialize a vector to capture the change in time
time_vec = [];

ref_velocity = single([0.10,0.20,0.0,0.02,0.01,0.0]'); % Step input reference 0.05 magnitude
    
r = rosrate(200);% 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


J = galen_robot.galen_jacob();
while(current_sim_time<10)
    %Grab the current sampling point in time relative to total time
    current_sim_time = r.TotalElapsedTime;
    
    qdot_ref = J\ref_velocity;
    qdot_ref = [qdot_ref(1);qdot_ref(2);qdot_ref(3);qdot_ref(4);0;qdot_ref(5)];
    


    % 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;
    % Find the error between desired and output
    error = norm(current_velocity-qdot_ref);

    %Store input and output history
    cv(:,i) = current_velocity;
    rv(:,i) = ref_velocity;
    rqd(:,i) = qdot_ref;
    cqd(:,i) = qdot_out;
    % 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;


%% Apply Sine input on the robot

galen_robot.set_control_type('Velocity');  % setup velocity control

t = 0:pi/36:2*pi;%discretized input to have a period over 2pi separated by 36 parts
timer = 1;% Initialize starter time variable

r = rosrate(200);
current_sim_time =0;
reset(r)

while(current_sim_time<51)
    if timer>length(t)-1
        timer = 1;
    end
    upsign = 0.01*sin(t(timer));
    timer = 1 + timer;
    ref_velocity = single([upsign,upsign,upsign,0,0,0]');
     % Move the galen robot
    galen_robot.move_galen(ref_velocity);

    % Retrieve the current robot joints
    current_velocity = galen_robot.get_current_joints();
    % Find the error between desired and output
    error = norm(current_velocity-ref_velocity);

    %Store input and output history
    cv(1:3,i) = current_velocity(1:3);
    rv(1:3,i) = ref_velocity(1:3);

    % 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 = 1:length(cv); % 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

% 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')

%%


% 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


