diff --git a/Datasets/readme.txt b/Datasets/readme.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6199da8349541b86feca41a9012a9bc8512cdaae
--- /dev/null
+++ b/Datasets/readme.txt
@@ -0,0 +1,2 @@
+This folder is the default folder to store the datasets used for training the GATA shallow neural network
+using the provided matlab scripts. However, the path can be changed in the param_loader.m script.
diff --git a/GATANeuralNetTraining.m b/GATANeuralNetTraining.m
new file mode 100644
index 0000000000000000000000000000000000000000..9bdc15db8b7f497013f79364a363641994e4d913
--- /dev/null
+++ b/GATANeuralNetTraining.m
@@ -0,0 +1,52 @@
+function GATANeuralNetTraining()
+    % GATA T-IV classification of traversable lidar points using a shallow neural
+    % network
+
+    %% First, we load constants (labels and so on)
+    label_loader
+
+    %% Now, we load the user's parameters
+    param_loader    
+
+    %% Loading the datasets
+    [training_dataset, testing_dataset] = loadDataset(training_set_filename, testing_set_filename, training_testing_ratio, downsampling_ratio);
+
+    %% Now we fuse the classes to match the desired classification task
+    % NOTE: training and testing datasets are different, because they were
+    % from different files OR we have already splitted them, so we need to fuse
+    % classes in both datasets!
+    disp('Fusing KITTI ground truth classes in training set...')
+    [training_dataset] = fuseClasses(training_dataset, sink_classes_cell_array, ... 
+                                     source_classes_cell_array);
+    disp('Done!')
+
+    disp('Fusing KITTI ground truth classes in testing set...')
+    [testing_dataset] = fuseClasses(testing_dataset, sink_classes_cell_array, ... 
+                                    source_classes_cell_array);
+    disp('Done!')
+
+%%
+% Now we train the classifier
+disp('Training a Shallow Neural Network model for classification...')
+[nn_model, tr_x, tr_y, training_Pct_Err] = neural_network_classificator_training(training_dataset, ...
+                                                    desired_features_indices, classes_names, weight_classes, ...
+                                                    hiddenLayerSize, trainFcn, performFcn);
+disp('Done!')
+
+%%
+disp('Using NN for predicting classes...')
+[nn_classificated_dataset] = use_nn_classificator(nn_model, ... 
+    testing_dataset, desired_features_indices);  
+ 
+disp('Done!')
+
+%% Now we evaluate the system performance
+disp('Computing performance statistics...')
+[precission, recall, f1_score, overall_precission, ...
+    overall_recall, overall_f1_score, IoU] = ... 
+    evaluating_segmentation_results(nn_classificated_dataset);
+disp('Done!')
+
+%% Finally we export the model weights to csv file to use with GATA in ROS
+save_model_to_csv(nn_model, neural_net_filename);
+end
diff --git a/Neural_Nets_Weights/readme.txt b/Neural_Nets_Weights/readme.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7acae324a95ca9862e5aa9011bec53cc939c2654
--- /dev/null
+++ b/Neural_Nets_Weights/readme.txt
@@ -0,0 +1 @@
+This folder is the default folder to store the shallow neural networks weights to be used in the GATA ROS node. However, the path can be changed in the param_loader.m script.
diff --git a/evaluating_segmentation_results.m b/evaluating_segmentation_results.m
new file mode 100644
index 0000000000000000000000000000000000000000..7d032fe46d2bee8b2819e3252ae822f46ffd934a
--- /dev/null
+++ b/evaluating_segmentation_results.m
@@ -0,0 +1,39 @@
+function [precission, recall, f1_score, overall_precission, overall_recall, ...
+    overall_f1_score, IoU] = evaluating_segmentation_results(dataset)
+    disp('Evaluating segmentation results...')
+    
+    %% First we load constants (labels and so on)
+    label_loader
+    
+    %%
+    % We extract the predictions
+    predicted_classes = dataset.c(:);
+    
+    % And the labels
+    gt_classes = dataset.GTC(:);
+
+    figure 
+   
+    confusion_matrix = confusionchart(gt_classes, predicted_classes);
+
+    confusion_matrix.ColumnSummary = 'column-normalized';
+    confusion_matrix.RowSummary = 'row-normalized';
+    confusion_matrix.Title = ' Confusion Matrix';
+    
+    [m,order] = confusionmat(gt_classes,predicted_classes);
+    Diagonal  = diag(m);
+    
+    sum_rows       = sum(m,2);
+    recall         = Diagonal./sum_rows;
+    overall_recall = mean(recall);
+    
+    sum_col           = sum(m,1);
+    precission        = Diagonal./sum_col';
+    overall_precission = mean(precission);
+
+    f1_score = 2 * ((precission .* recall) ./ (precission + recall));
+    overall_f1_score = mean (f1_score);
+     
+%   IoU = (TP) / (TP + FP + FN);
+    IoU = Diagonal ./ (sum_rows + sum_col' - Diagonal);
+end
\ No newline at end of file
diff --git a/fuseClasses.m b/fuseClasses.m
new file mode 100644
index 0000000000000000000000000000000000000000..16d6bef9ca9a2300502574369ce5289e819e2769
--- /dev/null
+++ b/fuseClasses.m
@@ -0,0 +1,22 @@
+function [dataset_fused_labels] = ... 
+    fuseClasses(dataset, sink_classes_cell_array, ... 
+    source_classes_cell_array) 
+  
+  label_loader
+
+  dataset_fused_labels = dataset;
+
+  dataset_fused_labels.GTC(:) = NON_TRAVERSABLE;
+      
+  number_of_sink_classes = height(sink_classes_cell_array{1});
+  for i = 1:number_of_sink_classes
+    current_sink_class = sink_classes_cell_array{1}(i);
+    number_of_source_classes_for_current_sink = length(source_classes_cell_array{i});
+    for j = 1:number_of_source_classes_for_current_sink
+        class_to_fuse = source_classes_cell_array{i}(j);
+        source_indices = find(dataset.GTC == class_to_fuse);
+        dataset_fused_labels.GTC(source_indices) = current_sink_class;
+    end
+  end
+
+end
\ No newline at end of file
diff --git a/label_loader.m b/label_loader.m
new file mode 100644
index 0000000000000000000000000000000000000000..edccd2d17a684d64e3947924db0ae880f837b0ef
--- /dev/null
+++ b/label_loader.m
@@ -0,0 +1,27 @@
+% Load of constants
+
+% GATA T-IV
+% GATA labels
+GROUND = 46;
+OBSTACLE = 100;
+OVERHANGING_OBSTACLE =  15;
+
+% SemanticKITTI labels
+% Ground classes
+ROAD = 40; 
+SIDEWALK = 48;
+PARKING = 44;
+OTHER_GROUND = 49;
+LANE_MARKING = 60;
+TERRAIN = 72;
+VEGETATION = 70; 
+
+% For binary classification tasks we can use the following labels
+TRAVERSABLE = 1;
+NON_TRAVERSABLE = 0;
+
+% to use with NN one hot encoding
+NN_ROAD = 1;
+NN_SIDEWALK = 2;
+NN_TERRAIN = 3;
+NN_VEGETATION = 4;
diff --git a/loadDataset.m b/loadDataset.m
new file mode 100644
index 0000000000000000000000000000000000000000..143777d2e5532f08f0eeff4eaf19f7997ee2f663
--- /dev/null
+++ b/loadDataset.m
@@ -0,0 +1,68 @@
+function [training_dataset, testing_dataset] = loadDataset(training_set_filename, testing_set_filename, training_testing_ratio, downsampling_ratio)
+disp('Loading dataset from file...')
+
+use_same_seq_for_training_and_testing = true;
+if(training_set_filename ~= testing_set_filename)
+  use_same_seq_for_training_and_testing = false;
+end
+
+if(use_same_seq_for_training_and_testing)
+  disp('Loading dataset from file... using the same dataset for training and testing')
+
+  training_and_testing_dataset_filename = training_set_filename;
+  dataset = parseDataset(training_and_testing_dataset_filename);
+  disp('Done!')
+    
+  % once you have the data loaded in a table format, we can continue with the
+  % process
+  disp('Removing outliers and other non evaluable points...')
+  filtered_dataset = remove_non_evaluable_points(dataset);
+  clear dataset
+  disp('Done!')
+
+  if ~exist('training_testing_ratio','var')
+    disp('WARNING training / test ratio not specified, using default value of 0.7...')
+    training_testing_ratio = 0.7;
+  end
+    
+  % Now we split the dataset between train and test
+  disp('Splitting the dataset into training and testing sets')
+  dataset_length = height(filtered_dataset);
+  training_dataset_length = floor(training_testing_ratio * dataset_length);
+  training_filtered_dataset_not_down = filtered_dataset(1:training_dataset_length, :);
+  testing_filtered_dataset_not_down = filtered_dataset(training_dataset_length + 1:dataset_length, :);
+  clear filtered_dataset
+
+  disp('Done!')
+else
+  disp('Loading dataset from file... using different datasets for training and testing!')
+  training_raw_dataset = parseDataset(training_set_filename);
+  testing_raw_dataset = parseDataset(testing_set_filename);
+  disp('Done!')
+  % once you have the data loaded in a table format, we can continue with the
+  % process
+  disp('Removing outliers and other non evaluable points...')
+  training_filtered_dataset_not_down = remove_non_evaluable_points(training_raw_dataset);
+  testing_filtered_dataset_not_down = remove_non_evaluable_points(testing_raw_dataset);
+  clear training_raw_dataset
+  clear testing_raw_dataset
+  disp('Done!')
+end
+
+use_downsampling = true;
+if (~exist('downsampling_ratio', 'var') || downsampling_ratio < 2)
+  use_downsampling = false;
+end
+
+if(use_downsampling)
+  training_dataset = training_filtered_dataset_not_down(1:downsampling_ratio:end, :);
+  testing_dataset = testing_filtered_dataset_not_down(1:downsampling_ratio:end, :);
+else
+  training_dataset = training_filtered_dataset_not_down;
+  testing_dataset = testing_filtered_dataset_not_down;
+end
+
+clear training_filtered_dataset_not_down
+clear testing_filtered_dataset_not_down
+
+end
\ No newline at end of file
diff --git a/neural_network_classificator_training.m b/neural_network_classificator_training.m
new file mode 100644
index 0000000000000000000000000000000000000000..e916bc399e30b6fbd0dbb4425be6c7328e659ca5
--- /dev/null
+++ b/neural_network_classificator_training.m
@@ -0,0 +1,47 @@
+function [Mdl, tr_x, tr_y, training_Pct_Err] = neural_network_classificator_training(dataset, ... 
+    desired_features_indices, classes_names, weight_classes, ...
+    hiddenLayerSize, trainFcn, performFcn)
+    %% First we load constants (labels and so on)
+    label_loader
+
+    %%
+    dataset_for_classification = dataset;
+    training_X = [table2array(dataset_for_classification(:,desired_features_indices))];
+    training_Y = dataset_for_classification.GTC;
+    
+    rng('default')
+    tr_x = training_X';
+    tr_y = training_Y';
+
+    if(weight_classes)
+        for i=0:length(classes_names)-1
+            w(i+1) = length(find(tr_y == i))/length(tr_y);
+        end
+        w = 1 ./ w;
+        w = w / sum(w);
+    end
+    
+    for i=0:length(classes_names)-1
+        classes_values(i+1) = i;
+    end
+
+    tr_y = categorical(tr_y, classes_values, classes_names, "Ordinal",true);
+    tr_y = onehotencode(tr_y, 1, "ClassNames",classes_names);
+    
+    net = patternnet(hiddenLayerSize, trainFcn, performFcn);
+
+    net = configure(net,tr_x,tr_y);
+    
+    if(weight_classes)
+        [Mdl, tr, output] = train(net, tr_x, tr_y, [],[],w');
+    else
+        [Mdl, tr, output] = train(net, tr_x, tr_y);
+    end
+    
+    trueclass = vec2ind(tr_y);
+    N = length(trueclass);
+    assignedclass = vec2ind(output);
+    Nerr = sum(assignedclass~=trueclass);
+ 
+    training_Pct_Err = 100*Nerr/N
+end
diff --git a/param_loader.m b/param_loader.m
new file mode 100644
index 0000000000000000000000000000000000000000..d8793ec6adab85e0533a11458ab092fcb9f02770
--- /dev/null
+++ b/param_loader.m
@@ -0,0 +1,82 @@
+% Load of config parameteres
+neural_net_filename = './Neural_Nets_Weights/vegetation_and_terrain_are_non_traversable.csv';
+
+% If both files are the same, we will split it into two parts
+training_set_filename = './Datasets/probando_repo_dataset.csv';
+testing_set_filename  = './Datasets/probando_repo_dataset.csv';
+
+% This value is only used if we need to split the training dataset
+% (not used if there are two separate files for training and testing)
+training_testing_ratio = 0.7;
+
+% Taking into account that the neural net is small, we can decide 
+% to not use the whole training dataset. With the downsample ratio
+% we read the dataset in order and take one out of downsampling_ratio value
+% It is not random sampling
+%
+% Pointwise downsampling is only applied if its ratio is >= 2
+downsampling_ratio = 10;
+
+weight_classes = true;  % To give weight inversely to the class frequency
+                        % In T-IV paper it was not use, so the default value 
+                        % is false
+
+hiddenLayerSize = [39]; % Number of neurons in hidden layer. The length of
+                        % this vector is the number of hidden layers
+
+trainFcn = 'trainbr'; % See patternnet documentation if you want to use 
+performFcn = 'sse';   % other functions
+
+% Now we specify the features that we want to use (this sets the number of
+% neurons in the input layer. Our GATA algorithm extracts 13 features and
+% the best results are obtained using them all. However if faster
+% performance is required one can try to eliminate some of them (We
+% recommend to use the MATLAB's Classification Learner App to find the less 
+% informative features and then change this vector accordingly 
+desired_features_indices = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
+
+
+% Classification configuration: Please comment / uncomment / modify the code
+% as required by your application. 
+% The idea is that we will fuse labels using the information provided here. 
+% All the classes not especified will be labeled as NON_TRAVERSABLE.
+% The number of neurons in the output layer is dictated by the length of 
+% the sink_classes_cell_array, or in other words, the number of classes is
+% equal to the its length. 
+
+% % Multiclass example
+% % In this example, we consider ROAD, PARKING, LANE_MARKING and
+% % OTHER_GROUND to belong to the same class (with label 1), SIDEWALK is
+% % will have label 2, TERRAIN is 3 and VEGETATION is 4. So the neural net
+% % will have 4 outputs
+% sink_classes_cell_array = {};
+% sink_classes_cell_array{1} = [1;2;3;4]; % Always use consecutive numbers 
+%                                         % starting in 1 
+% source_classes_cell_array = {};
+% source_classes_cell_array{1} = [ROAD, PARKING, LANE_MARKING, OTHER_GROUND];
+% source_classes_cell_array{2} = [SIDEWALK];
+% source_classes_cell_array{3} = [TERRAIN];
+% source_classes_cell_array{4} = [VEGETATION];
+% % Now we put the categorical names for the Patternnet (class 0 is always
+% % 'obstacle', choose the names for the rest of the classes
+% classes_names = {'obstacle' 'road' 'sidewalk' 'terrain' 'vegetation'};
+
+% Binary classification examples
+% We want VEGETATION and TERRAIN to be considered as NON_TRAVERSABLE
+% So we put the traversable label to all the other ground classes  
+sink_classes_cell_array = {};
+sink_classes_cell_array{1} = [TRAVERSABLE]; % TRAVERSABLE is label 1
+source_classes_cell_array = {};
+source_classes_cell_array{1} = [ROAD, SIDEWALK, PARKING, LANE_MARKING, OTHER_GROUND];
+classes_names = {'obstacle' 'traversable'};
+
+% % Other binary example: Only ROAD is traversable
+% % For this example we just need to put label TRAVERSABLE for ROAD points 
+% % and leave the rest as NON_TRAVERSABLE
+% sink_classes_cell_array = {};
+% sink_classes_cell_array{1} = [TRAVERSABLE]; % TRAVERSABLE is label 1
+% source_classes_cell_array = {};
+% source_classes_cell_array{1} = [ROAD];
+% classes_names = {'obstacle' 'traversable'};
+
+
diff --git a/parseDataset.m b/parseDataset.m
new file mode 100644
index 0000000000000000000000000000000000000000..6f638a409b2b79bc43a47cc00482996175b7b485
--- /dev/null
+++ b/parseDataset.m
@@ -0,0 +1,10 @@
+function dataset = parseDataset(filename)
+  dataset = readtable(filename);
+  dataset = renamevars(dataset,["Var1","Var2","Var3","Var4","Var5","Var6" ...
+      ,"Var7","Var8","Var9","Var10","Var11","Var12","Var13", "Var14"], ...
+                 ["squared_dist_point_sensor", "incidence_angle", "intensity", ... 
+                 "squared_dist_point_ref", "pred_error", "score", "ratio", ... 
+                 "mean_intensity", "var_intensity", "mean_pred_error", ... 
+                 "var_pred_error", "mean_score", "var_score", "GTC"]);
+end
+
diff --git a/remove_non_evaluable_points.m b/remove_non_evaluable_points.m
new file mode 100644
index 0000000000000000000000000000000000000000..09e5416f89f7285262182cd44b15b7de847263df
--- /dev/null
+++ b/remove_non_evaluable_points.m
@@ -0,0 +1,17 @@
+function [filtered_dataset] = remove_non_evaluable_points(dataset)
+    % we first filter out the non evaluable points
+    UNLABELED = 0;
+    OUTLIER = 1;
+    OTHER_STRUCTURE = 52;
+    OTHER_OBJECT = 99;
+    
+    filtered_dataset = dataset(dataset.GTC ~= UNLABELED, :);
+    filtered_dataset = filtered_dataset(filtered_dataset.GTC ~= OUTLIER, :);
+    filtered_dataset = filtered_dataset(filtered_dataset.GTC ~= OTHER_STRUCTURE, :);
+    filtered_dataset = filtered_dataset(filtered_dataset.GTC ~= OTHER_OBJECT, :);
+    
+    % We filter some extrange rows that are all filled with zeros
+    %filtered_dataset = filtered_dataset(filtered_dataset.c ~= UNLABELED, :); 
+    
+    %TOTAL_POINTS = height(filtered_dataset)
+end
\ No newline at end of file
diff --git a/save_model_to_csv.m b/save_model_to_csv.m
new file mode 100644
index 0000000000000000000000000000000000000000..0015afe3e688d31c21d6ff9d1add7c3950cfcf81
--- /dev/null
+++ b/save_model_to_csv.m
@@ -0,0 +1,17 @@
+function save_model_to_csv(nn_classificator_model, nn_csv_name)
+  xoffset=nn_classificator_model.inputs{1}.processSettings{1}.xoffset;
+  gain=nn_classificator_model.inputs{1}.processSettings{1}.gain;
+  ymin=nn_classificator_model.inputs{1}.processSettings{1}.ymin;
+  w1 = nn_classificator_model.IW{1};   
+  w2 = nn_classificator_model.LW{2};   
+  b1 = nn_classificator_model.b{1};    
+  b2 = nn_classificator_model.b{2};
+
+  writematrix(b1, nn_csv_name);
+  writematrix(b2, nn_csv_name, 'WriteMode','append');
+  writematrix(gain, nn_csv_name, 'WriteMode','append');
+  writematrix(w1, nn_csv_name, 'WriteMode','append');
+  writematrix(w2, nn_csv_name, 'WriteMode','append');
+  writematrix(xoffset, nn_csv_name, 'WriteMode','append');
+  writematrix(ymin, nn_csv_name, 'WriteMode','append');
+end
\ No newline at end of file
diff --git a/use_nn_classificator.m b/use_nn_classificator.m
new file mode 100644
index 0000000000000000000000000000000000000000..da255e7c1761ca195519cda45055516a0e82bade
--- /dev/null
+++ b/use_nn_classificator.m
@@ -0,0 +1,67 @@
+function [classified_dataset] = use_nn_classificator ...
+    (nn_model, classified_dataset, desired_features_indices)
+    %% First we load constants (labels and so on)
+    label_loader
+    
+    %%
+    X = [table2array(classified_dataset(:,desired_features_indices))];
+
+    X = X';
+    Ymod = nn_model(X);
+
+    %% This is a manual computation to check that we can do the prediction
+    %  exactly as the Matlab model does. This is important because we will
+    %  export the weights to a .csv file and do these computations in C++
+    %  in the ROS node.
+    xoffset=nn_model.inputs{1}.processSettings{1}.xoffset;
+    gain=nn_model.inputs{1}.processSettings{1}.gain;
+    ymin=nn_model.inputs{1}.processSettings{1}.ymin;
+    w1 = nn_model.IW{1};   
+    w2 = nn_model.LW{2};   
+    b1 = nn_model.b{1};    
+    b2 = nn_model.b{2};
+    % Input 1
+    y1 = bsxfun(@times,bsxfun(@minus,X,xoffset),gain);
+    y1 = bsxfun(@plus,y1,ymin);
+    % Layer 1
+    a1 = 2 ./ (1 + exp(-2*(repmat(b1,1,size(X,2)) + w1*y1))) - 1;
+    % output
+    n=repmat(b2,1,size(X,2)) + w2*a1;
+    nmax = max(n,[],1);
+    n = bsxfun(@minus,n,nmax);
+    num = exp(n);
+    den = sum(num,1);
+    den(den == 0) = 1;
+    Y = bsxfun(@rdivide,num,den);
+
+    %%%% just a check!
+    first_obs = X(:,1);
+    x_offset_applied = first_obs - xoffset;
+    x_gain_applied = x_offset_applied .* gain;
+    x_final = x_gain_applied + ymin;
+
+    a1_first = (2 ./ (1 + exp((-2 * (b1 + w1 * x_final))))) - 1;
+    n_first = b2 + w2 * a1_first;
+    nmax_first = max(n_first);
+    n_first = n_first - nmax_first;
+    num_first = exp(n_first);
+    den_first = sum(num_first);
+    y_first = num_first / den_first;
+
+    error = sum(y_first - Y(:,1));
+    whole_error = Y - Ymod;
+    %%%
+
+    % Now we need to decode the network output
+    max_outputs = max(Y);
+    idx = (Y == max_outputs);
+
+    labels = [];
+    for i = 0:height(Y)-1
+      labels(i+1) = i;
+    end
+
+    idx_decoded = (idx' * labels')';
+  
+    classified_dataset(:, "c") = num2cell(idx_decoded');
+end
\ No newline at end of file