diff --git a/python/iLQR_manipulator_obstacle.py b/python/iLQR_manipulator_obstacle.py
index b8e57445cf4df642e51561060976bb1f237acb4f..503f71d2fdcef5eff4ec0ef12435ec16a56d1db1 100644
--- a/python/iLQR_manipulator_obstacle.py
+++ b/python/iLQR_manipulator_obstacle.py
@@ -3,6 +3,7 @@
 
     Copyright (c) 2021 Idiap Research Institute, http://www.idiap.ch/
     Written by Jeremy Maceiras <jeremy.maceiras@idiap.ch>,
+    Teguh Lembono <teguh.lembono@idiap.ch>,
     Sylvain Calinon <https://calinon.ch>
 
     This file is part of RCFS.
@@ -24,6 +25,8 @@ import numpy as np
 import numpy.matlib
 import matplotlib.pyplot as plt
 import matplotlib.patches as patches
+import time
+
 
 # Helper functions
 # ===============================
@@ -37,12 +40,12 @@ def logmap(f,f0):
 
 # Forward kinematics for E-E
 def fkin(x,ls):
-	x = x.T
-	A = np.tril(np.ones([len(ls),len(ls)]))
-	f = np.vstack((ls @ np.cos(A @ x), 
-				   ls @ np.sin(A @ x), 
-				   np.mod(np.sum(x,0)+np.pi, 2*np.pi) - np.pi)) #x1,x2,o (orientation as single Euler angle for planar robot)
-	return f.T
+    x = x.T
+    A = np.tril(np.ones([len(ls),len(ls)]))
+    f = np.vstack((ls @ np.cos(A @ x), 
+                    ls @ np.sin(A @ x), 
+                    np.mod(np.sum(x,0)+np.pi, 2*np.pi) - np.pi)) #x1,x2,o (orientation as single Euler angle for planar robot)
+    return f.T
 
 # Forward Kinematics for all joints
 def fkin0(x):
@@ -58,6 +61,18 @@ def fkin0(x):
         ))
     return f
 
+# Forward Kinematics for all joints in batch
+def fkin0batch(x):
+    global param
+    T = np.tril(np.ones([param.nbVarX,param.nbVarX]))
+    T2 = np.tril(np.matlib.repmat(param.linkLengths,x.shape[1],1))
+    f = np.vstack(( 
+        (T2 @ np.cos(T@x.T)).flatten(order='F'),
+        (T2 @ np.sin(T@x.T)).flatten(order='F')
+        )).T
+    return f
+
+# Cost and jacobians for avoiding obstacles
 def f_avoid(x):
     global param
     f=[]
@@ -83,12 +98,47 @@ def f_avoid(x):
                     J2[-J_tmp.shape[0]:,-J_tmp.shape[1]:] = J_tmp
                     J = J2 # Numpy does not provid a blockdiag function...
 
-                    idx.append( (t-1)*param.nbVarU + np.array(range(param.nbVarU)) )
+                    idx.append( t*param.nbVarU + np.array(range(param.nbVarU)) )
 
     f = np.array(f)
     idx = np.array(idx)
     return f,J.T,idx
 
+# Cost and jacobians for avoiding obstacles, computed in batch
+def f_avoid2(x):
+    global param
+    f, J, idx = [], [], []
+    
+    for i in range(param.nbObstacles):
+        f_x = fkin0batch(x)                
+        e = (f_x- param.Obs[i][:2])@param.U_obs[i]        
+        ftmp = 1 - np.linalg.norm(e, axis=1)**2
+        
+        #check for value larger than zeros
+        idx_active = np.arange(len(ftmp))[ftmp>0]
+        
+        #compute Jacobian for each idx_active
+        for j in idx_active:
+            t = j//param.nbVarX # which time step
+            k = j%param.nbVarX  # which link
+            
+            J_rbt = np.hstack((jkin(x[t,:(k+1)] , param.linkLengths[:(k+1)]), np.zeros(( param.nbVarF , param.nbVarX-(k+1))) ))
+            J_tmp = (-e[j].T @ param.U_obs[i].T @ J_rbt[:2])
+            J_j = np.zeros(param.nbVarX*param.nbData)
+            J_j[param.nbVarX*t: param.nbVarX*(t+1)] = J_tmp
+            J += [J_j]            
+            
+            #Get indices
+            idx += np.arange(param.nbVarX*t, param.nbVarX*(t+1)).tolist()
+        f += [ftmp[idx_active]]
+    
+    idx = np.array(list(set(idx)))
+    f = np.concatenate(f)
+    if len(idx) > 0:
+        J = np.vstack(J)
+        J = J[:,idx]
+    return f, J,idx
+
 def jkin(x,ls):
     global param
     T = np.tril( np.ones((len(ls),len(ls))) )
@@ -127,6 +177,7 @@ param.sizeObstacle = [.5,.8] # Size of objects
 param.r = 1e-3 # Control weight term
 param.Q_track = 1e2
 param.Q_avoid = 1e0
+param.useBatch = True #Batch computation for collision avoidance cost
 
 # Task parameters
 # ===============================
@@ -183,13 +234,16 @@ Su = Su0[idx.flatten()] # We remove the lines that are out of interest
 
 # Solving iLQR
 # ===============================
-
+tic=time.time()
 for i in range( param.nbIter ):
     x = Su0 @ u + Sx0 @ x0
     x = x.reshape( (  param.nbData , param.nbVarX) )
 
     f, J = f_reach(x[tl])  # Tracking objective
-    f2, J2, id2 = f_avoid(x)# Avoidance objective
+    if param.useBatch:
+        f2, J2, id2 = f_avoid2(x)# Avoidance objective
+    else:
+        f2, J2, id2 = f_avoid(x)# Avoidance objective
 
     if len(id2) > 0: # Numpy does not allow zero sized array as Indices
         Su2 = Su0[id2.flatten()]
@@ -207,7 +261,11 @@ for i in range( param.nbIter ):
         xtmp = Su0 @ utmp + Sx0 @ x0
         xtmp = xtmp.reshape( (  param.nbData , param.nbVarX) )
         ftmp, _ = f_reach(xtmp[tl])
-        f2tmp,_,_ = f_avoid(xtmp)
+        if param.useBatch:
+            f2tmp,_,_ = f_avoid2(xtmp)
+        else:
+            f2tmp,_,_ = f_avoid(xtmp)
+            
         cost = np.linalg.norm(ftmp.flatten())**2 * param.Q_track + np.linalg.norm(f2tmp.flatten())**2 * param.Q_avoid + np.linalg.norm(utmp) * param.r
 
         if cost < cost0 or alpha < 1e-3:
@@ -218,8 +276,9 @@ for i in range( param.nbIter ):
         alpha /=2
     
     if np.linalg.norm(alpha * du) < 1e-2: # Early stop condition
-       break
-
+        break
+toc=time.time()
+print('Solving in {} seconds'.format(toc-tic))
 # Ploting
 # ===============================