Examples
========

This page provides complete, working examples for common use cases.
Examples are shown in both server mode (sync) and async mode where applicable.

Example 1: Simple Motion (Server Mode)
---------------------------------------

Move the robot through positions using the sync API:

.. code-block:: python

   import numpy as np
   import aiofranka
   from aiofranka import FrankaRemoteController

   aiofranka.unlock()

   controller = FrankaRemoteController()
   controller.start()

   # Define waypoints
   home = [0, 0, 0, -1.57079, 0, 1.57079, -0.7853]
   pose1 = [0, -0.785, 0, -2.356, 0, 1.571, 0.785]

   # Move through waypoints
   for pose in [home, pose1, home]:
       print(f"Moving to: {pose}")
       controller.move(pose)

   controller.stop()
   aiofranka.lock()

Example 2: Simple Motion (Async Mode)
--------------------------------------

Same motion using the async API:

.. code-block:: python

   import asyncio
   import numpy as np
   from aiofranka import RobotInterface, FrankaController

   async def simple_motion():
       robot = RobotInterface("172.16.0.2")
       controller = FrankaController(robot)

       await controller.start()

       try:
           home = [0, 0, 0, -1.57079, 0, 1.57079, -0.7853]
           pose1 = [0, -0.785, 0, -2.356, 0, 1.571, 0.785]

           for pose in [home, pose1, home]:
               print(f"Moving to: {pose}")
               await controller.move(pose)
               await asyncio.sleep(1.0)

       finally:
           await controller.stop()

   if __name__ == "__main__":
       asyncio.run(simple_motion())

Example 3: Impedance Control
------------------------------

Compliant joint-space control with sinusoidal motion:

.. code-block:: python

   import numpy as np
   import aiofranka
   from aiofranka import FrankaRemoteController

   aiofranka.unlock()

   controller = FrankaRemoteController()
   controller.start()

   # Move to start position
   controller.move()

   # Configure impedance control
   controller.switch("impedance")
   controller.kp = np.ones(7) * 80.0
   controller.kd = np.ones(7) * 4.0
   controller.set_freq(50)

   # Execute smooth sinusoidal motion
   for i in range(200):  # 4 seconds at 50 Hz
       delta = np.sin(i / 50.0 * np.pi) * 0.1
       target = controller.initial_qpos + delta
       controller.set("q_desired", target)

   controller.stop()
   aiofranka.lock()

Example 4: Operational Space Control
--------------------------------------

Control end-effector position in Cartesian space:

.. code-block:: python

   import numpy as np
   import aiofranka
   from aiofranka import FrankaRemoteController

   aiofranka.unlock()

   controller = FrankaRemoteController()
   controller.start()

   controller.move()

   # Configure OSC
   controller.switch("osc")
   controller.ee_kp = np.array([300, 300, 300, 1000, 1000, 1000])
   controller.ee_kd = np.ones(6) * 10.0
   controller.set_freq(50)

   # Circular motion in XY plane
   for i in range(200):
       angle = i / 50.0 * np.pi
       radius = 0.05

       desired_ee = controller.initial_ee.copy()
       desired_ee[0, 3] += radius * np.cos(angle)
       desired_ee[1, 3] += radius * np.sin(angle)

       controller.set("ee_desired", desired_ee)

   controller.stop()
   aiofranka.lock()

Example 5: Data Collection
----------------------------

Collect synchronized robot data during operation:

.. code-block:: python

   import numpy as np
   import time
   import aiofranka
   from aiofranka import FrankaRemoteController

   aiofranka.unlock()

   controller = FrankaRemoteController()
   controller.start()
   controller.move()

   controller.switch("impedance")
   controller.kp = np.ones(7) * 80.0
   controller.kd = np.ones(7) * 4.0
   controller.set_freq(50)

   logs = {'qpos': [], 'qvel': [], 'qdes': [], 'ctrl': [], 'ee': []}

   for i in range(200):
       state = controller.state
       logs['qpos'].append(state['qpos'].copy())
       logs['qvel'].append(state['qvel'].copy())
       logs['ctrl'].append(state['last_torque'].copy())
       logs['ee'].append(state['ee'].copy())
       logs['qdes'].append(controller.q_desired.copy())

       delta = np.sin(i / 50.0 * np.pi) * 0.1
       controller.set("q_desired", delta + controller.initial_qpos)

   for key in logs:
       logs[key] = np.array(logs[key])

   np.savez("robot_data.npz", **logs)
   print(f"Saved {len(logs['qpos'])} samples")

   controller.stop()
   aiofranka.lock()

Example 6: Gain Tuning
------------------------

Systematically test different controller gains:

.. code-block:: python

   import numpy as np
   import time
   import os
   import aiofranka
   from aiofranka import FrankaRemoteController

   aiofranka.unlock()

   controller = FrankaRemoteController()
   controller.start()

   base = np.array([1, 1, 1, 1, 0.6, 0.6, 0.6])
   kps = [16, 32, 64, 128, 256]
   kds = [1, 2, 4, 8, 16]

   for kp in kps:
       for kd in kds:
           # Move to start
           controller.kp = base * 80
           controller.kd = base * 4
           controller.move()
           time.sleep(1.0)

           print(f"Testing kp={kp}, kd={kd}")
           controller.switch("impedance")
           controller.kp = base * kp
           controller.kd = base * kd
           controller.set_freq(50)

           logs = {'qpos': [], 'qdes': []}
           for cnt in range(200):
               state = controller.state
               logs['qpos'].append(state['qpos'].copy())
               logs['qdes'].append(controller.q_desired.copy())

               delta = np.sin(cnt / 50.0 * np.pi) * 0.1
               controller.set("q_desired", delta + controller.initial_qpos)

           for key in logs:
               logs[key] = np.stack(logs[key])

           os.makedirs("sysid_data", exist_ok=True)
           np.savez(f"sysid_data/K{kp}_D{kd}.npz", **logs)

   controller.stop()
   aiofranka.lock()

Example 7: End-Effector Configuration
---------------------------------------

Set end-effector mass and center of mass for accurate gravity compensation:

.. code-block:: python

   import aiofranka

   aiofranka.unlock()

   # Set end-effector parameters (mass in kg, CoM in meters)
   aiofranka.set_configuration(mass=1.0, com=[0, 0, 0.057])

   # Now start your control script...
   # The robot will use the updated parameters for gravity compensation

   aiofranka.lock()

Example 8: Gripper Control
---------------------------

Control a Robotiq gripper alongside the robot arm:

.. code-block:: python

   import asyncio
   from aiofranka import GripperController

   async def gripper_demo():
       gripper = GripperController("/dev/ttyUSB1")
       await gripper.start()

       # Set speed and force (like kp/kd for the arm)
       gripper.speed = 128
       gripper.force = 200

       # Open and close
       gripper.q_desired = 0     # Open
       await gripper.wait_until_reached()

       gripper.q_desired = 255   # Close
       await gripper.wait_until_reached()

       # Or use convenience methods
       gripper.open()
       await gripper.wait_until_reached()

       gripper.close()
       await gripper.wait_until_reached()

       await gripper.stop()

   asyncio.run(gripper_demo())

.. note::
   Gripper support requires: ``pip install "aiofranka[robotiq]"``

Example 9: Simulation Testing
-------------------------------

Test your controller in simulation before deploying to real robot (async mode only):

.. code-block:: python

   import asyncio
   import numpy as np
   from aiofranka import RobotInterface, FrankaController

   async def test_algorithm(robot_ip=None):
       robot = RobotInterface(robot_ip)
       controller = FrankaController(robot)

       await controller.start()

       try:
           mode = "SIMULATION" if robot_ip is None else "REAL"
           print(f"Testing in {mode} mode")

           await controller.move()

           controller.switch("impedance")
           controller.kp = np.ones(7) * 80.0
           controller.kd = np.ones(7) * 4.0
           controller.set_freq(50)

           for i in range(100):
               delta = np.sin(i / 50.0 * np.pi) * 0.1
               target = controller.initial_qpos + delta
               await controller.set("q_desired", target)

           print("Test successful!")

       finally:
           await controller.stop()

   if __name__ == "__main__":
       # First test in simulation
       asyncio.run(test_algorithm(None))

       # Then deploy to real robot
       asyncio.run(test_algorithm("172.16.0.2"))

More Examples
-------------

For more examples, check the ``examples/`` directory in the repository:

- ``01_collect_ref_traj.py``: System identification data collection (async mode)
- ``01_collect_ref_traj_remote.py``: System identification data collection (server mode)
- ``02_spacemouse_teleop.py``: SpaceMouse teleoperation with cameras (async mode)
- ``02_spacemouse_teleop_remote.py``: SpaceMouse teleoperation with cameras (server mode)
- ``03_sysid.py``: System identification with CMA-ES optimization
- ``04_gripper.py``: Robotiq gripper toggle demo
- ``05_gripper_sysid.py``: Gripper system identification

Next Steps
----------

- Review :doc:`controllers` for detailed controller documentation
- Check :doc:`cli` for robot setup commands
- Explore the :doc:`async_mode` guide if using async mode
