diff --git a/lerobot/common/robot_devices/motors/trossen_arm_driver.py b/lerobot/common/robot_devices/motors/trossen_arm_driver.py
index 77e1465b..cc06be3c 100644
--- a/lerobot/common/robot_devices/motors/trossen_arm_driver.py
+++ b/lerobot/common/robot_devices/motors/trossen_arm_driver.py
@@ -10,14 +10,12 @@ from lerobot.common.robot_devices.utils import (
     RobotDeviceNotConnectedError,
 )
 
-PITCH_CIRCLE_RADIUS = 0.00875 # meters
-VEL_LIMITS = [3.375, 3.375, 3.375, 7.0, 7.0, 7.0, 12.5 * PITCH_CIRCLE_RADIUS]
-
 TROSSEN_ARM_MODELS = {
     "V0_LEADER": [trossen.Model.wxai_v0, trossen.StandardEndEffector.wxai_v0_leader],
     "V0_FOLLOWER": [trossen.Model.wxai_v0, trossen.StandardEndEffector.wxai_v0_follower],
 }
 
+
 class TrossenArmDriver:
     """
         The `TrossenArmDriver` class provides an interface for controlling
@@ -63,7 +61,6 @@ class TrossenArmDriver:
         ```
     """
 
-
     def __init__(
         self,
         config: TrossenArmDriverConfig,
@@ -79,7 +76,7 @@ class TrossenArmDriver:
         self.home_pose = [0, np.pi/3, np.pi/6, np.pi/5, 0, 0, 0]
         self.sleep_pose = [0, 0, 0, 0, 0, 0, 0]
 
-        self.motors={
+        self.motors = {
                     # name: (index, model)
                     "joint_0": [1, "4340"],
                     "joint_1": [2, "4340"],
@@ -90,15 +87,7 @@ class TrossenArmDriver:
                     "joint_6": [7, "4310"],
                 }
 
-        self.prev_write_time = 0
-        self.current_write_time = None
-
-        # To prevent DiscontinuityError due to large jumps in position in short time.
-        # We scale the time to move based on the distance between the start and goal values and the maximum speed of the motors.
-        # The below factor is used to scale the time to move.
-        self.TIME_SCALING_FACTOR = 3.0
-
-        # Minimum time to move for the arm (This is a tuning parameter)
+        # Minimum time to move for the arm
         self.MIN_TIME_TO_MOVE = 3.0 / self.fps
 
     def connect(self):
@@ -138,7 +127,6 @@ class TrossenArmDriver:
         # Allow to read and write
         self.is_connected = True
 
-
     def reconnect(self):
         try:
             model_name, model_end_effector = TROSSEN_ARM_MODELS[self.model]
@@ -155,7 +143,6 @@ class TrossenArmDriver:
 
         self.is_connected = True
 
-
     @property
     def motor_names(self) -> list[str]:
         return list(self.motors.keys())
@@ -197,6 +184,8 @@ class TrossenArmDriver:
             values = self.driver.get_positions()
             values[:-1] = np.degrees(values[:-1])  # Convert all joints except gripper
             values[-1] = values[-1] * 10000  # Convert gripper to range (0-450)
+        elif data_name == "External_Efforts":
+            values = self.driver.get_external_efforts()
         else:
             values = None
             print(f"Data name: {data_name} is not supported for reading.")
@@ -208,16 +197,6 @@ class TrossenArmDriver:
         values = np.array(values, dtype=np.float32)
         return values
 
-    def compute_time_to_move(self, goal_values: np.ndarray):
-        # Compute the time to move based on the distance between the start and goal values
-        # and the maximum speed of the motors
-        current_pose = self.driver.get_positions()
-        displacement = abs(goal_values - current_pose)
-        time_to_move_all_joints = self.TIME_SCALING_FACTOR*displacement / VEL_LIMITS
-        time_to_move = max(time_to_move_all_joints)
-        time_to_move = max(time_to_move, self.MIN_TIME_TO_MOVE)
-        return time_to_move
-
     def write(self, data_name, values: int | float | np.ndarray, motor_names: str | list[str] | None = None):
         if not self.is_connected:
             raise RobotDeviceNotConnectedError(
@@ -232,8 +211,7 @@ class TrossenArmDriver:
             # Convert back to radians for joints
             values[:-1] = np.radians(values[:-1])  # Convert all joints except gripper
             values[-1] = values[-1] / 10000  # Convert gripper back to range (0-0.045)
-            self.driver.set_all_positions(values.tolist(), self.compute_time_to_move(values), False)
-            self.prev_write_time = self.current_write_time
+            self.driver.set_all_positions(values.tolist(), self.MIN_TIME_TO_MOVE, False)
 
         # Enable or disable the torque of the motors
         elif data_name == "Torque_Enable":
@@ -248,6 +226,8 @@ class TrossenArmDriver:
             self.driver.set_all_velocities([0.0] * self.driver.get_num_joints(), 0.0, False)
             self.driver.set_all_modes(trossen.Mode.position)
             self.driver.set_all_positions(self.home_pose, 2.0, False)
+        elif data_name == "External_Efforts":
+            self.driver.set_all_external_efforts(values, 0.0, False)
         else:
             print(f"Data name: {data_name} value: {values} is not supported for writing.")
 
diff --git a/lerobot/common/robot_devices/robots/configs.py b/lerobot/common/robot_devices/robots/configs.py
index e45daee6..ecb27ef7 100644
--- a/lerobot/common/robot_devices/robots/configs.py
+++ b/lerobot/common/robot_devices/robots/configs.py
@@ -630,6 +630,11 @@ class TrossenAIStationaryRobotConfig(ManipulatorRobotConfig):
     # then to gradually add more motors (by uncommenting), until you can teleoperate both arms fully
     max_relative_target: int | None = 5
 
+    # Gain applied to external efforts sensed on the follower arm and transmitted to the leader arm.
+    # This enables the user to feel external forces (e.g., contact with objects) through force feedback.
+    # A value of 0.0 disables force feedback. A good starting value for a responsive experience is 0.1.
+    force_feedback_gain: float = 0.0
+
     leader_arms: dict[str, MotorsBusConfig] = field(
         default_factory=lambda: {
             "left": TrossenArmDriverConfig(
@@ -653,7 +658,7 @@ class TrossenAIStationaryRobotConfig(ManipulatorRobotConfig):
             ),
             "right": TrossenArmDriverConfig(
                 ip="192.168.1.4",
-                model = "V0_FOLLOWER",
+                model="V0_FOLLOWER",
             ),
         }
     )
@@ -709,6 +714,11 @@ class TrossenAISoloRobotConfig(ManipulatorRobotConfig):
     # then to gradually add more motors (by uncommenting), until you can teleoperate both arms fully
     max_relative_target: int | None = 5
 
+    # Gain applied to external efforts sensed on the follower arm and transmitted to the leader arm.
+    # This enables the user to feel external forces (e.g., contact with objects) through force feedback.
+    # A value of 0.0 disables force feedback. A good starting value for a responsive experience is 0.1.
+    force_feedback_gain: float = 0.0
+
     leader_arms: dict[str, MotorsBusConfig] = field(
         default_factory=lambda: {
             "main": TrossenArmDriverConfig(
@@ -754,9 +764,24 @@ class TrossenAISoloRobotConfig(ManipulatorRobotConfig):
 @RobotConfig.register_subclass("trossen_ai_mobile")
 @dataclass
 class TrossenAIMobileRobotConfig(RobotConfig):
-    
+
+    # /!\ FOR SAFETY, READ THIS /!\
+    # `max_relative_target` limits the magnitude of the relative positional target vector for safety purposes.
+    # Set this to a positive scalar to have the same value for all motors, or a list that is the same length as
+    # the number of motors in your follower arms.
+    # For Trossen AI Arms, for every goal position request, motor rotations are capped at 5 degrees by default.
+    # When you feel more confident with teleoperation or running the policy, you can extend
+    # this safety limit and even removing it by setting it to `null`.
+    # Also, everything is expected to work safely out-of-the-box, but we highly advise to
+    # first try to teleoperate the grippers only (by commenting out the rest of the motors in this yaml),
+    # then to gradually add more motors (by uncommenting), until you can teleoperate both arms fully
     max_relative_target: int | None = 5
 
+    # Gain applied to external efforts sensed on the follower arm and transmitted to the leader arm.
+    # This enables the user to feel external forces (e.g., contact with objects) through force feedback.
+    # A value of 0.0 disables force feedback. A good starting value for a responsive experience is 0.1.
+    force_feedback_gain: float = 0.0
+
     enable_motor_torque: bool = False
 
     leader_arms: dict[str, MotorsBusConfig] = field(
@@ -782,7 +807,7 @@ class TrossenAIMobileRobotConfig(RobotConfig):
             ),
             "right": TrossenArmDriverConfig(
                 ip="192.168.1.4",
-                model = "V0_FOLLOWER",
+                model="V0_FOLLOWER",
             ),
         }
     )
diff --git a/lerobot/common/robot_devices/robots/manipulator.py b/lerobot/common/robot_devices/robots/manipulator.py
index 452abbb1..bbc0f323 100644
--- a/lerobot/common/robot_devices/robots/manipulator.py
+++ b/lerobot/common/robot_devices/robots/manipulator.py
@@ -165,6 +165,7 @@ class ManipulatorRobot:
         self.leader_arms = make_motors_buses_from_configs(self.config.leader_arms)
         self.follower_arms = make_motors_buses_from_configs(self.config.follower_arms)
         self.cameras = make_cameras_from_configs(self.config.cameras)
+        self.force_feedback_gain = self.config.force_feedback_gain
         self.is_connected = False
         self.logs = {}
 
@@ -494,6 +495,8 @@ class ManipulatorRobot:
 
             goal_pos = goal_pos.numpy().astype(np.float32)
             self.follower_arms[name].write("Goal_Position", goal_pos)
+            external_efforts = -1 * self.force_feedback_gain * self.follower_arms[name].read("External_Efforts")
+            self.leader_arms[name].write("External_Efforts", external_efforts)
             self.logs[f"write_follower_{name}_goal_pos_dt_s"] = time.perf_counter() - before_fwrite_t
 
         # Early exit when recording data is not requested