diff --git a/matlab/iLQR_distMaintenance.m b/matlab/iLQR_distMaintenance.m
new file mode 100644
index 0000000000000000000000000000000000000000..13439f408932042ad704ed19d5ab344cfe5594c7
--- /dev/null
+++ b/matlab/iLQR_distMaintenance.m
@@ -0,0 +1,109 @@
+%%    iLQR applied to a 2D point-mass system with the objective of constantly  
+%%    maintaining a desired distance to an object 
+%%
+%%    Copyright (c) 2022 Idiap Research Institute, https://www.idiap.ch/
+%%    Written by Sylvain Calinon <https://calinon.ch>
+%%
+%%    This file is part of RCFS.
+%%
+%%    RCFS is free software: you can redistribute it and/or modify
+%%    it under the terms of the GNU General Public License version 3 as
+%%    published by the Free Software Foundation.
+%%
+%%    RCFS is distributed in the hope that it will be useful,
+%%    but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%%    GNU General Public License for more details.
+%%
+%%    You should have received a copy of the GNU General Public License
+%%    along with RCFS. If not, see <http://www.gnu.org/licenses/>.
+
+function iLQR_distMaintenance
+
+%% Parameters
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+param.dt = 1E-2; %Time step size
+param.nbData = 50; %Number of datapoints
+param.nbIter = 100; %Maximum number of iterations for iLQR
+param.nbVarX = 2; %State space dimension (x1,x2)
+param.nbVarU = 2; %Control space dimension (dx1,dx2)
+param.Mu = [1.0; 0.3]; %Object location
+param.dist = .4; %Distance to maintain
+param.q = 1E0; %Distance maintenance weight term
+param.r = 1E-3; %Control weight term
+
+R = speye((param.nbData-1) * param.nbVarU) * param.r; %Control weight matrix (at trajectory level)
+
+
+%% Iterative LQR (iLQR)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+u = zeros(param.nbVarU*(param.nbData-1), 1); %Initial commands
+x0 = zeros(param.nbVarX, 1); %Initial state
+
+%Transfer matrices (for linear system as single integrator)
+Su = [zeros(param.nbVarX, param.nbVarX*(param.nbData-1)); tril(kron(ones(param.nbData-1), eye(param.nbVarX)*param.dt))];
+Sx = kron(ones(param.nbData,1), eye(param.nbVarX));
+
+for n=1:param.nbIter
+	x = reshape(Su * u + Sx * x0, param.nbVarX, param.nbData); %System evolution
+	[f, J] = f_dist(x, param); %Residuals and Jacobians (distance maintenance objective)
+	
+	du = (Su' * J' * J * Su * param.q + R) \ (-Su' * J' * f(:) * param.q - u * param.r); %Gauss-Newton update
+	
+	%Estimate step size with backtracking line search method
+	alpha = 1;
+	cost0 = norm(f(:))^2 * param.q + norm(u)^2 * param.r; %Cost
+	while 1
+		utmp = u + du * alpha;
+		xtmp = reshape(Su * utmp + Sx * x0, param.nbVarX, param.nbData);
+		ftmp = f_dist(xtmp, param); %Residuals (avoidance objective)
+		cost = norm(ftmp(:))^2 * param.q + norm(utmp)^2 * param.r; %Cost
+		if cost < cost0 || alpha < 1E-3
+			break;
+		end
+		alpha = alpha * 0.5;
+	end
+	u = u + du * alpha;
+	
+	if norm(du * alpha) < 5E-2
+		break; %Stop iLQR when solution is reached
+	end
+end
+disp(['iLQR converged in ' num2str(n) ' iterations.']);
+
+
+%% Plot state space
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+al = linspace(-pi, pi, 50);
+h = figure('position',[10,10,800,800],'color',[1,1,1]); hold on; axis off;
+msh = param.dist * [cos(al); sin(al)] + repmat(param.Mu(1:2), 1, 50);
+patch(msh(1,:), msh(2,:), [1 .8 .8],'linewidth',2,'edgecolor',[.8 .4 .4]);
+plot(param.Mu(1), param.Mu(2), '.','markersize',25,'color',[.8 0 0]);
+plot(x(1,:), x(2,:), '-','linewidth',2,'color',[0 0 0]);
+plot(x(1,[1,end]), x(2,[1,end]), '.','markersize',25,'color',[0 0 0]);
+axis equal; 
+
+waitfor(h);
+end 
+
+%%%%%%%%%%%%%%%%%%%%%%
+%Residuals f and Jacobians J for maintaining a desired distance to an object (fast version with computation in matrix form)
+function [f, J] = f_dist(x, param)
+	e = (x - repmat(param.Mu(1:2), 1, param.nbData)) / param.dist;
+	f = 1 - sum(e.^2, 1)'; %Residuals
+	Jtmp = -e' * repmat(eye(param.nbVarU)/param.dist, 1, param.nbData); 
+	J = Jtmp .* kron(eye(param.nbData), ones(1,param.nbVarU)); %Jacobians 
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%
+%%%Residuals f and Jacobians J for obstacle avoidance (slow version with computation using a loop over time steps)
+%function [f, J] = f_dist(x, param)
+%	f=[]; J=[]; 
+%	for t=1:size(x,2)
+%		e = (x(:,t) - param.Mu(1:2)) / param.dist;
+%		ftmp = 1 - e' * e; 
+%		f = [f; ftmp]; %Residuals
+%		Jtmp = -e' / param.dist; 
+%		J = blkdiag(J, Jtmp); %Jacobians
+%	end
+%end
diff --git a/matlab/iLQR_obstacle.m b/matlab/iLQR_obstacle.m
index 7837266d190e75737de1c61aa9f22120dbc7d2f7..b36c748baa23a146f9ab9e6878f1e75367114015 100644
--- a/matlab/iLQR_obstacle.m
+++ b/matlab/iLQR_obstacle.m
@@ -131,22 +131,23 @@ function [f, J, id, idt] = f_avoid(x, param)
 		id = [id, (idt-1) * param.nbVarU + [1:param.nbVarU]']; %Corresponding position indices when inside obstacles 
 		f = [f; ftmp(idt)]; %Residuals
 		Jtmp = -e(:,idt)' * repmat(param.U2(:,:,i)', 1, length(idt));
+		Jtmp = Jtmp .* kron(eye(length(idt)), ones(1,param.nbVarU));
 		J = blkdiag(J, Jtmp); %Jacobians
 	end
 end
 
 %%%%%%%%%%%%%%%%%%%%%%%
-%%Residuals f and Jacobians J for obstacle avoidance (slow version with loop over time)
+%%Residuals f and Jacobians J for obstacle avoidance (slow version with computation using a loop over time steps)
 %function [f, J, id, idt] = f_avoid(x, param)
 %	f=[]; J=[]; id=[]; idt=[];
 %	for t=1:size(x,2)
 %		for i=1:param.nbObstacles			
 %			e = param.U2(:,:,i)' * (x(:,t) - param.Mu2(1:2,i));
-%			ftmp = 1 - e' * e; %quadratic form
+%			ftmp = 1 - e' * e; 
 %			%Bounding boxes 
 %			if ftmp > 0
 %				f = [f; ftmp]; %Residuals
-%				Jtmp = -e' * param.U2(:,:,i)'; %quadratic form
+%				Jtmp = -e' * param.U2(:,:,i)'; 
 %				J = blkdiag(J, Jtmp); %Jacobians
 %				idt = [idt, t]; %Time indices when inside obstacles 
 %				id = [id, (t-1) * param.nbVarU + [1:param.nbVarU]]; %Corresponding position indices when inside obstacles