# Teleoperation Robot

The **Teleoperation Robot** allows users to **control a robot remotely** using a variety of input methods such as a joystick, keyboard, or even a mobile application. This project is designed to enhance **human-robot interaction**, making it ideal for **remote inspection, assistance, and industrial automation**.

#### **Key Features:**

* **Real-Time Remote Control:** Operate the robot from a distance with low-latency response.
* **Multiple Control Interfaces:** Supports **joysticks, keyboards, and wireless controllers**.
* **Live Data Feedback:** Real-time sensor data monitoring for better decision-making.
* **Versatile Applications:** Used in **hazardous environments, remote assistance, and exploration**.

[**Discover More About the Teleoperation Robot**](/smd-applications/robotics/teleoperation-robot.md)

**code**

{% code lineNumbers="true" %}

```python
from pynput import keyboard
import time
from smd.red import *
from serial.tools.list_ports import comports
from platform import system

class PIDController:

    def __init__(self, kp, ki, kd):
        self.kp = kp
        self.ki = ki
        self.kd = kd
        self.previous_error = 0
        self.integral = 0

    def calculate(self, error, delta_time):
        self.integral += error * delta_time
        derivative = (error - self.previous_error) / delta_time if delta_time > 0 else 0
        output = (self.kp * error) + (self.ki * self.integral) + (self.kd * derivative)
        self.previous_error = error
        return max(min(output, 100), -100)  
    
    def USB_Port():
        ports = list(comports())
        usb_names = {
            "Windows": ["USB Serial Port"],
            "Linux": ["/dev/ttyUSB"],
            "Darwin": [
                "/dev/tty.usbserial",
                "/dev/tty.usbmodem",
                "/dev/tty.SLAB_USBtoUART",
                "/dev/tty.wchusbserial",
                "/dev/cu.usbserial",
            ]
        }
        os_name = system()
        if ports:
            for port, desc, hwid in sorted(ports):
                if any(name in port or name in desc for name in usb_names.get(os_name, [])):
                    print("Connected!")
                    return port
            print("Available ports:")
            for port, desc, hwid in ports:
                print(f"Port: {port}, Description: {desc}, HWID: {hwid}")
        else:
            print("No ports detected!")
        return None
    
    def teleoperate_smd():
        print("Use W/A/S/D to control the robot. Press Q to quit.")
    
        port = USB_Port()
        if not port:
            print("No suitable port found. Exiting...")
            return
    
        try:
            smd = Master(port)
            smd.attach(Red(0))  # Left motor (ID 0)
            smd.attach(Red(1))  # Right motor (ID 1)
            
            smd.enable_torque(0, 1)
            smd.enable_torque(1, 1)
    
            left_pid = PIDController(kp=24.96, ki=0.00, kd=19.10)
            right_pid = PIDController(kp=46.97, ki=0.00, kd=18.96)
    
            base_speed = 100  
            turning_speed = 100  # Speed for turning
            last_time = time.time()
    
            def on_press(key):
                nonlocal last_time
    
                try:
                    # Calculate time difference
                    current_time = time.time()
                    delta_time = current_time - last_time
                    last_time = current_time
    
                    if hasattr(key, 'char'):
                        if key.char == 'w':  # Move forward
                            print("Move Forward")
                            error = base_speed
                            left_speed = left_pid.calculate(error, delta_time)
                            right_speed = right_pid.calculate(error, delta_time)
                            # Send commands to both motors simultaneously
                            smd.set_duty_cycle(0, -left_speed)  # Left motor forward
                            smd.set_duty_cycle(1, right_speed)  # Right motor forward
                        elif key.char == 's':  # Move backward
                            print("Move Backward")
                            error = -base_speed
                            left_speed = left_pid.calculate(error, delta_time)
                            right_speed = right_pid.calculate(error, delta_time)
                            # Send commands to both motors simultaneously
                            smd.set_duty_cycle(0, -left_speed)  # Left motor backward
                            smd.set_duty_cycle(1, right_speed)  # Right motor backward
                        elif key.char == 'a':  # Turn left
                            print("Turn Left")
                            # Send commands to both motors simultaneously
                            smd.set_duty_cycle(0, 0)  # Left motor stopped
                            smd.set_duty_cycle(1, turning_speed)  # Right motor forward
                        elif key.char == 'd':  # Turn right
                            print("Turn Right")
                            # Send commands to both motors simultaneously
                            smd.set_duty_cycle(0, -turning_speed)  # Left motor forward
                            smd.set_duty_cycle(1, 0)  # Right motor stopped
                        elif key.char == 'q':  # Quit
                            print("Exiting...")
                            return False
                except AttributeError:
                    pass

        def on_release(key):
            # Stop both motors simultaneously
            smd.set_duty_cycle(0, 0)
            smd.set_duty_cycle(1, 0)

        # Start keyboard listener
        with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
            listener.join()

    except Exception as e:
        print(f"Error: {e}")
    finally:
        # Stop both motors simultaneously during cleanup
        smd.set_duty_cycle(0, 0)
        smd.set_duty_cycle(1, 0)
        smd.enable_torque(0, 0)
        smd.enable_torque(1, 0)
        smd.close()
        print("SMD connection closed.")

teleoperate_smd()

```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.acrome.net/smd-applications/robotics/differential-robot-projects/teleoperation-robot.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
