# Telefon

Robotunuzu telefonunuzla (iOS veya Android) kontrol edin.

**Bu kılavuzda şunları öğreneceksiniz:**

- iOS/Android telefonu nasıl bağlanır
- Telefonun duruşunun robotun uç efektor (EE) hedeflerine nasıl eşlendiği
- Güvenlik sınırlarını, kavrama kontrolünü ve IK ayarlarını nasıl ayarlayabilirsiniz

Robotunuzu telefonla kontrol etmek için ilgili bağımlılıkları şununla yükleyin:

```bash
pip install 'lerobot[phone,kinematics,feetech,core_scripts]'
```

`kinematics`, örneklerin kullandığı ters kinematiği; `feetech`, SO-100/SO-101 motorlarını; `core_scripts` ise kayıt ve görselleştirme araçlarını içerir.

## Başlayın

### Desteklenen platformlar

- iOS: HEBI Mobile I/O uygulamasını (ARKit duruş + düğmeler) kullanır. Önce uygulamayı indirin, açın ve örnekler ağınızda keşfederek telefonun duruşunu ve girdilerini aktaracaktır.
- Android: `teleop` paketini (WebXR) kullanır. Python sürecini başlattığınızda yerel bir URL yazdırılır. Bağlantıyı telefonunuzda açın, Başlat'a dokunun ve ardından Move'u kullanarak duruşu (pose) yayınlayın.

Bağlantılar:

- Android WebXR kütüphanesi: [PyPI'de `teleop`](https://pypi.org/project/teleop/)
- iOS uygulaması: [HEBI Mobile I/O](https://docs.hebi.us/tools.html#mobile-io)

### Telefon yönlendirmesi ve kontroller

- Yerleşim: Telefonu ekranı yukarı bakacak ve üst kenarı robot kavrayıcısının yönüyle aynı yöne bakacak şekilde tutun. Bu, kalibrasyonun telefonun çerçevesini robot çerçevesiyle hizalamasını ve hareketin doğal hissedilmesini sağlar; referans için aşağıdaki görseli inceleyin.
- Etkinleştir/devre dışı bırak — bu bir **ölü adam anahtarıdır, tüm hareket boyunca basılı tutmanız gerekir**:
  - iOS: `B1` tuşuna basılı tutarak robotu hareket ettirin, bırakınca kolun bulunduğu konumda dondurun. Her basış, telefonu kolun mevcut duruşuna yeniden sabitler; böylece bırakabilir, elinizi rahat bir konuma geri getirebilir, tekrar basıp devam edebilirsiniz — robot zıplamaz.
  - Android: WebXR sayfasındaki `Move` düğmesiyle aynı.
- Gripper kontrolü (enable düğmesine basılı tutulup tutulmadığından bağımsız olarak çalışır):
  - iOS: Analog kaydırıcı `A3`, gripper'ı hız girişi olarak kontrol eder — yukarı iterek açın, aşağı çekerek kapatın, ortada bırakarak sabitleyin.
  - Android: `A` ve `B` düğmeleri arttırma/azaltma gibi çalışır (A açar, B kapatır). Hızı `GripperVelocityToJoint` adımında ayarlayabilirsiniz.
- Tutuş: Telefonu **her iki elinizle** tutun, bir başparmağınızı etkinleştirme düğmesinin (`B1`) üzerinde, diğerini ise kavrama kaydırıcısının (`A3`) üzerinde tutun; böylece kavrayışı değiştirmeden kolu ve kavrayıcıyı aynı anda sürdürebilirsiniz. Bunu yaparken telefonu düz tutun — duruşu komuttur, bu yüzden hareket sırasında tutuşu değiştirmek robotu hareket ettirir.
- Robot başlangıç pozisyonu: `teleoperate.py`, başlangıçta (telefon kalibre edilmeden önce) kolu [homing pozisyonuna](./so101#calibrate) taşır ve Ctrl+C ile betiğin başladığı anda bulunduğu konuma geri döndürür. Homing komutları, kalibrasyon sırasında `set_half_turn_homings()` tarafından merkezlenen yarım dönüş değerine her gövde eklemine gönderilir — kalibrasyon şemasında tutulan pozisyon — bu sırada gripper olduğu yerde bırakılır, böylece homing işlemi asla bir şeyin üzerine kapanmaz. Bu önemlidir çünkü hedefler, etkinleştirme düğmesine bastığınız anda kolun bulunduğu konumdan IK ile çözülür — katlanmış veya tamamen uzatılmış bir kolun az alanı kalır ve eklem sınırlarına takılır. Diğer örnek betikler (`record.py`, `evaluate.py`, `replay.py`) kolu homing yapmaz, bu nedenle onları başlatmadan önce kendiniz oraya yerleştirin.

### Adım 1: Platformu seçin

Örnekleri `PhoneConfig` içinde `PhoneOS.IOS` veya `PhoneOS.ANDROID` kullanacak şekilde değiştirin. API tüm platformlarda aynıdır, yalnızca girdi kaynağı farklıdır. Tüm örnekler `examples/phone_to_so100/` dizinindedir: `teleoperate.py`, `record.py`, `replay.py`, `evaluate.py` ve `rollout.py`.

Teleoperasyon örneği:

```python
from lerobot.teleoperators.phone import Phone, PhoneConfig
from lerobot.teleoperators.phone.config_phone import PhoneOS

teleop_config = PhoneConfig(phone_os=PhoneOS.IOS)  # or PhoneOS.ANDROID
teleop_device = Phone(teleop_config)
```

### Adım 2: Bağlayın ve kalibre edin

`Phone(teleop_config)` oluşturulduğunda ve `connect()` çağrıldığında kalibrasyon otomatik olarak istenir. Telefonu yukarıda açıklanan konumda tutun, ardından:

- iOS: Referans pozisyonu yakalamak için `B1` tuşuna basılı tutun.
- Android: WebXR sayfasında referans pozunu yakalamak için `Move` düğmesine basın.

Neden kalibre edilir? Mevcut duruşu yakalayarak sonraki duruşların robot hizalı bir çerçevede ifade edilmesini sağlarız. Kontrolü etkinleştirmek için düğmeye tekrar bastığınızda, devre dışıyken telefonunuz yeniden konumlandırıldığında kaymayı önlemek için konum yeniden yakalanır.

### Adım 3: Bir örnek çalıştırın

Teleoperasyon yapmak, bir veri seti kaydetmek, bir veri setini yeniden oynatmak veya bir politikayı değerlendirmek için örnek scriptlerden birini çalıştırın.

Tüm betikler, robotunuzun yapılandırıldığını ve kalibre edildiğini (ör. SO-100 takipçi) varsayar.

**Robotun URDF dosyasını sağlamanız gerekir.** Her örnek ters kinematiği çözer; bu işlem kolun kinematik modelini gerektirir ve URDF, LeRobot ile birlikte dağıtılmaz.

Bu öğreticide kullanılan SO-100/SO-101 için, LeRobot deposunda olduğunuzda [SO-ARM100 deposu](https://github.com/TheRobotStudio/SO-ARM100/tree/main/Simulation/SO101)ndaki `SO101` klasörünü `examples/phone_to_so100/` içine kopyalayın; böylece dosya `examples/phone_to_so100/SO101/so101_new_calib.urdf` konumuna yerleşir:

```bash
git clone --depth 1 https://github.com/TheRobotStudio/SO-ARM100.git /tmp/SO-ARM100
cp -r /tmp/SO-ARM100/Simulation/SO101 examples/phone_to_so100/SO101
```

Dosyayı başka bir yerde saklıyorsanız veya başka bir kolu kullanıyorsanız, betikleri `--urdf` ile farklı bir dosyaya yönlendirin.

- Teleoperasyon için bu örneği çalıştırın ve takipçi kolunuzun seri portunu (bulmak için `lerobot-find-port` kullanın) iletin:

  ```bash
  cd examples/phone_to_so100
  python teleoperate.py --port /dev/tty.usbmodem5A460814411
  ```

  `--id`, kullanılacak depolanan kalibrasyonu seçer ve `--urdf`, yukarıdaki URDF dosyasına işaret eder.

Örneği çalıştırdıktan sonra:

- Android: Betiği başlattıktan sonra, telefonunuzda yazdırılan yerel URL'yi açın, Başlat'a dokunun, ardından Move'u basılı tutun.
- iOS: Önce HEBI Mobile I/O uygulamasını açın; robotu hareket ettirmek için B1 tuşuna basılı tutun. A3 gripper'ı kontrol eder.

Ayrıca, örneklerde gösterilen işlemci adımlarını düzenleyerek eşlemeyi veya güvenlik sınırlarını özelleştirebilirsiniz. Girdileri yeniden eşleyerek (ör. farklı bir analog giriş kullanarak) veya girdi ile kinematik adımlarını değiştirerek hattı diğer robotlara (ör. LeKiwi) uyarlayabilirsiniz. Bununla ilgili daha fazla bilgi [Robotlar ve Teleoperatörler için İşlemciler](./processors_robots_teleop) kılavuzunda yer almaktadır.

- Bir veri seti kaydetmek için bu örneği çalıştırın; bu işlem mutlak uç efektor gözlemlerini ve eylemlerini kaydeder:

  ```bash
  cd examples/phone_to_so100
  python record.py
  ```

- Kaydedilen bölümleri yeniden oynatmak için bu örneği çalıştırın:

  ```bash
  cd examples/phone_to_so100
  python replay.py
  ```

- Önceden eğitilmiş bir politikayı değerlendirmek için bu örneği çalıştırın:

  ```bash
  cd examples/phone_to_so100
  python evaluate.py
  ```

### Önemli iş akışı adımları ve seçenekleri

- Kinematikler birden fazla adımda kullanılır. Kinematiklerimizi işlemek için Pinocchio'nun bir sarmalayıcısı olan [Placo](https://github.com/Rhoban/placo) kullanıyoruz. Robotun URDF'sini ve hedef çerçevesini geçirerek kinematik nesnesini oluşturuyoruz. `target_frame_name` değerini gripper çerçevesine ayarlıyoruz.

  ```python
  kinematics_solver = RobotKinematics(
    urdf_path="./SO101/so101_new_calib.urdf",
    target_frame_name="gripper_frame_link",
    joint_names=list(robot.bus.motors.keys()),
  )

  ```

- `MapPhoneActionToRobotAction` adımı, kalibre edilmiş telefon pozunu ve girdileri hedef deltalar ve gripper komutlarına dönüştürür; adımın çıktısı aşağıda gösterilmektedir.

  ```python
  action["enabled"] = enabled
  action["target_x"] = pos[1] if enabled else 0.0
  action["target_y"] = -pos[0] if enabled else 0.0
  action["target_z"] = pos[2] if enabled else 0.0
  action["target_wx"] = rotvec[1] if enabled else 0.0
  action["target_wy"] = -rotvec[0] if enabled else 0.0
  action["target_wz"] = rotvec[2] if enabled else 0.0
  action["gripper_vel"] = gripper_vel  # Still send gripper action when disabled
  ```

- `EEReferenceAndDelta` adımı, hedef deltaları mutlak bir EE pozu isteğine dönüştürür ve etkinleştirme sırasında bir referans saklar; `end_effector_step_sizes`, EE pozu için adım boyutlarıdır ve hareket hızını değiştirmek için değiştirilebilir.

  ```python
  EEReferenceAndDelta(
      kinematics=kinematics_solver,
      end_effector_step_sizes={"x": 0.5, "y": 0.5, "z": 0.5},
      motor_names=list(robot.bus.motors.keys()),
      use_latched_reference=True,
  ),
  ```

- `EEBoundsAndSafety` adımı, EE hareketini bir çalışma alanına sınırlar ve güvenlik sağlamak için büyük EE adım sıçramalarını kontrol eder. `end_effector_bounds`, EE duruşu için sınırlardır ve çalışma alanını değiştirmek için değiştirilebilir. `max_ee_step_m`, EE duruşu için adım sınırlarıdır ve güvenlik sınırlarını değiştirmek için değiştirilebilir.

  ```python
  EEBoundsAndSafety(
      end_effector_bounds={"min": [-1.0, -1.0, -1.0], "max": [1.0, 1.0, 1.0]},
      max_ee_step_m=0.10,
  )
  ```

- `GripperVelocityToJoint` adımı, mevcut ölçülen durumu kullanarak hız benzeri bir kavrama girişini mutlak kavrama konumuna dönüştürür. `speed_factor`, hızın çarpıldığı katsayıdır.

  ```python
  GripperVelocityToJoint(speed_factor=20.0)
  ```

#### Farklı IK başlangıç tahminleri

Kine matik adımlarda farklı IK başlangıç tahminleri kullanıyoruz. Başlangıç tahmini olarak ya mevcut ölçülen eklem konumları ya da önceki IK çözümü kullanılır.

- Kapalı döngü (kayıt/değerlendirme sırasında kullanılır): IK'nin her karede ölçülen eklem noktalarından başlaması için `initial_guess_current_joints=True` olarak ayarlanır.

  ```python
  InverseKinematicsEEToJoints(
      kinematics=kinematics_solver,
      motor_names=list(robot.bus.motors.keys()),
      initial_guess_current_joints=True,  # closed loop
  )
  ```

- Açık döngü (replay'de kullanılır): `initial_guess_current_joints=False` olarak ayarlanır, böylece IK, ölçülen durumdan değil, önceki IK çözümünden devam eder. Bu, geri besleme olmadan replay sırasında eylem kararlılığını korur.

  ```python
  InverseKinematicsEEToJoints(
      kinematics=kinematics_solver,
      motor_names=list(robot.bus.motors.keys()),
      initial_guess_current_joints=False,  # open loop
  )
  ```

### Adımların açıklaması

- MapPhoneActionToRobotAction: kalibre edilmiş telefon duruşunu ve girdileri hedef deltaları ve bir kavrama komutuna dönüştürür. Hareket, bir etkinleştirme sinyaliyle (iOS'ta B1, Android'de Move) kontrol edilir.
- EEReferenceAndDelta: etkinleştirildiğinde bir referans EE duruşunu kilitler ve bunu hedef deltalarla birleştirerek her karede mutlak bir istenen EE duruşu üretir. Devre dışı bırakıldığında, son komut verilen duruşu göndermeye devam eder.
- EEBoundsAndSafety: EE duruşunu bir çalışma alanına sınırlar ve güvenlik için sıçramaları hız sınırlar. Ayrıca `action.ee.*` özelliklerini de tanımlar.
- InverseKinematicsEEToJoints: IK ile bir EE duruşunu eklem konumlarına dönüştürür. Kapalı döngü kontrol için `initial_guess_current_joints=True` önerilir; kararlılık için açık döngü oynatmada `False` olarak ayarlayın.
- GripperVelocityToJoint: mevcut ölçülen durumu kullanarak hız benzeri bir gripper girdisini mutlak gripper konumuna entegre eder.
- ForwardKinematicsJointsToEE: kayıt ve EE durumu üzerinde eğitim için gözlemlenen eklem noktalarından `observation.state.ee.*` değerlerini hesaplar.

### Sorun Giderme

- iOS algılanamadı: HEBI Mobile I/O'nun açık olduğundan ve laptop/telefonunuzun aynı ağda olduğundan emin olun.
- Android URL'ine ulaşılamıyor: yerel olarak `https` yerine `http` kullandığınızdan, betiğin yazdırdığı tam IP adresini kullandığınızdan ve tarayıcınızın giriş yapmasına izin verip sertifika sorununu yoksaydığından emin olun.
- Hareket ters gibi hissediyor: eksenleri tek tek kontrol edin, ardından `MapPhoneActionToRobotAction` içindeki işaret dönüşlerini ayarlayın veya kurulumunuza uygun olacak şekilde eksenleri yer değiştirin.
- Kol neredeyse hiç hareket etmez veya sınırlarına takılır: orta aralıklı bir başlangıç pozisyonundan yeniden başlatın (yukarıya bakın) — IK, kolun mevcut konfigürasyonundan çözülür.

