> ## Documentation Index
> Fetch the complete documentation index at: https://stem-docs.intellectualpoint.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Basic movement programs

> Learn the PiCar-X movement API, build a movement demo from scratch, then write a keyboard-controlled driving program.

## Basic movement programs

<Info>
  **Time**: 9:30 AM to 10:15 AM
</Info>

Today you start writing real code. By the end of this section you will understand every movement command the robot supports and have built two programs from scratch: an automated movement demo and a keyboard-controlled driver.

***

## Get the day 2 code

SSH into your robot and clone the camp scripts:

```bash theme={null}
ssh car@picar.local
git clone https://github.com/intellectual-point/robot-genai-camp.git ~/camp
cd ~/camp
ls
```

You should see all the scripts for this week. Today you will work with `basic_movement.py` and `keyboard_drive.py`, but first you are going to build them yourself so you understand every line.

***

## The movement API

Every program starts the same way — import the library and create a robot object:

```python theme={null}
from picarx import Picarx
import time

px = Picarx()
```

`px` is your robot. Here are the commands you can call on it:

| Command                         | What it does                                                        |
| ------------------------------- | ------------------------------------------------------------------- |
| `px.forward(speed)`             | Drive forward. Speed is 0–100 (percentage).                         |
| `px.backward(speed)`            | Drive backward.                                                     |
| `px.stop()`                     | Stop the motors.                                                    |
| `px.set_dir_servo_angle(angle)` | Turn the steering. Range: -35 (left) to +35 (right). 0 is straight. |

<Note>
  Every program that controls motors or servos must run with `sudo`:

  ```bash theme={null}
  sudo python3 my_script.py
  ```
</Note>

***

## Program 1: movement demo

Create a new file on the robot:

```bash theme={null}
nano ~/camp/day2/basic_movement.py
```

### Step 1 — Setup and imports

Start with the imports and create the robot object. Wrap everything in a `try/finally` block so the robot always stops cleanly, even if you press Ctrl-C:

```python theme={null}
#!/usr/bin/env python3
from picarx import Picarx
import sys
import time

px = Picarx()
```

### Step 2 — Straight lines

The simplest commands: drive forward, wait, stop, then reverse:

```python theme={null}
def run_demo():
    print("  1. Forward at 30% speed for 2 seconds")
    px.forward(30)
    time.sleep(2)
    px.stop()
    time.sleep(0.5)

    print("  2. Backward at 30% speed for 2 seconds")
    px.backward(30)
    time.sleep(2)
    px.stop()
    time.sleep(0.5)
```

`px.forward(30)` starts the motors at 30% power. They keep running until you call `px.stop()`. The `time.sleep(2)` makes the program wait 2 seconds while the motors run.

### Step 3 — Steering turns

To turn, set the steering angle **before** or **while** driving. Remember to reset it to 0 afterwards:

```python theme={null}
    print("  3. Turn left while moving forward")
    px.set_dir_servo_angle(-30)
    px.forward(30)
    time.sleep(2)
    px.stop()
    px.set_dir_servo_angle(0)
    time.sleep(0.5)

    print("  4. Turn right while moving forward")
    px.set_dir_servo_angle(30)
    px.forward(30)
    time.sleep(2)
    px.stop()
    px.set_dir_servo_angle(0)
    time.sleep(0.5)
```

Negative angles steer left, positive angles steer right. The maximum is ±35°.

### Step 4 — Speed ramp

Use a `for` loop to smoothly ramp the speed up and then back down:

```python theme={null}
    print("  5. Speed ramp: 10% → 60% → 10%")
    for speed in range(10, 65, 5):
        px.forward(speed)
        time.sleep(0.3)
    for speed in range(60, 5, -5):
        px.forward(speed)
        time.sleep(0.3)
    px.stop()
    time.sleep(0.5)
```

`range(10, 65, 5)` counts from 10 to 60 in steps of 5. Each step lasts 0.3 seconds, so the robot accelerates and then decelerates smoothly.

### Step 5 — Figure-8

Combine steering changes while driving to create a figure-8 pattern:

```python theme={null}
    print("  6. Figure-8 pattern")
    px.set_dir_servo_angle(-25)
    px.forward(30)
    time.sleep(3)
    px.set_dir_servo_angle(25)
    time.sleep(3)
    px.stop()
    px.set_dir_servo_angle(0)
    time.sleep(0.5)
```

The trick: you do not need to stop between turns. Just change the steering angle while the motors are still running.

### Step 6 — Three-point turn

A three-point turn uses forward, backward, and steering together:

```python theme={null}
    print("  7. Three-point turn")
    px.set_dir_servo_angle(-35)
    px.forward(30)
    time.sleep(1.5)
    px.stop()
    time.sleep(0.3)

    px.set_dir_servo_angle(35)
    px.backward(30)
    time.sleep(1.5)
    px.stop()
    time.sleep(0.3)

    px.set_dir_servo_angle(0)
    px.forward(30)
    time.sleep(1)
    px.stop()

    print("  Done!")
```

### Step 7 — Main block

Add the entry point that runs the demo safely:

```python theme={null}
if __name__ == "__main__":
    print("=" * 50)
    print("  PiCar-X — Basic Movement Demo")
    print("=" * 50)
    print("  Clear space around the robot.")
    print("  Press Ctrl-C at any time to stop.\n")
    input("  Press ENTER to start...")

    try:
        run_demo()
    except KeyboardInterrupt:
        print("\n  Stopped!")
    finally:
        px.stop()
        px.set_dir_servo_angle(0)
```

The `finally` block guarantees the motors stop and steering centers even if you interrupt the program.

### Run it

```bash theme={null}
sudo python3 ~/camp/day2/basic_movement.py
```

<Warning>
  Clear space around the robot before starting. The demo runs for about 30 seconds and the robot will move in all directions.
</Warning>

### Final code: basic\_movement.py

<Accordion title="Click to see the complete program">
  ```python theme={null}
  #!/usr/bin/env python3
  from picarx import Picarx
  import sys
  import time


  def run_demo(px):
      print("\n  === MOVEMENT DEMO ===\n")

      print("  1. Forward at 30% speed for 2 seconds")
      px.forward(30)
      time.sleep(2)
      px.stop()
      time.sleep(0.5)

      print("  2. Backward at 30% speed for 2 seconds")
      px.backward(30)
      time.sleep(2)
      px.stop()
      time.sleep(0.5)

      print("  3. Turn left while moving forward")
      px.set_dir_servo_angle(-30)
      px.forward(30)
      time.sleep(2)
      px.stop()
      px.set_dir_servo_angle(0)
      time.sleep(0.5)

      print("  4. Turn right while moving forward")
      px.set_dir_servo_angle(30)
      px.forward(30)
      time.sleep(2)
      px.stop()
      px.set_dir_servo_angle(0)
      time.sleep(0.5)

      print("  5. Speed ramp: 10% → 60% → 10%")
      for speed in range(10, 65, 5):
          px.forward(speed)
          time.sleep(0.3)
      for speed in range(60, 5, -5):
          px.forward(speed)
          time.sleep(0.3)
      px.stop()
      time.sleep(0.5)

      print("  6. Figure-8 pattern")
      px.set_dir_servo_angle(-25)
      px.forward(30)
      time.sleep(3)
      px.set_dir_servo_angle(25)
      time.sleep(3)
      px.stop()
      px.set_dir_servo_angle(0)
      time.sleep(0.5)

      print("  7. Three-point turn")
      px.set_dir_servo_angle(-35)
      px.forward(30)
      time.sleep(1.5)
      px.stop()
      time.sleep(0.3)

      px.set_dir_servo_angle(35)
      px.backward(30)
      time.sleep(1.5)
      px.stop()
      time.sleep(0.3)

      px.set_dir_servo_angle(0)
      px.forward(30)
      time.sleep(1)
      px.stop()

      print("\n  Done! All movements complete.\n")


  if __name__ == "__main__":
      print("=" * 50)
      print("  PiCar-X — Basic Movement Demo")
      print("=" * 50)
      print("  Clear space around the robot.")
      print("  Press Ctrl-C at any time to stop.\n")

      px = Picarx()
      input("  Press ENTER to start...")

      try:
          run_demo(px)
      except KeyboardInterrupt:
          print("\n  Stopped!")
      finally:
          px.stop()
          px.set_dir_servo_angle(0)
  ```
</Accordion>

***

## Program 2: keyboard drive

Now build an interactive driver where you control the robot in real time with WASD keys.

```bash theme={null}
nano ~/camp/day2/keyboard_drive.py
```

### Step 1 — Read keypresses without Enter

Normally Python waits for you to press Enter. This helper function reads a single keypress immediately:

```python theme={null}
#!/usr/bin/env python3
from picarx import Picarx
import sys
import time

def getch():
    """Read a single keypress without waiting for Enter."""
    import tty, termios
    fd = sys.stdin.fileno()
    old = termios.tcgetattr(fd)
    try:
        tty.setraw(fd)
        ch = sys.stdin.read(1)
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old)
    return ch
```

This puts the terminal in "raw mode" temporarily, reads one character, then restores the terminal. You do not need to understand every line here — just know that `getch()` returns whatever key the user pressed.

### Step 2 — Set up state variables

Track the robot's current state:

```python theme={null}
px = Picarx()
speed = 30
steer_angle = 0
steer_step = 10
moving = None   # None, 'forward', or 'backward'
```

### Step 3 — The control loop

Read a key, match it to an action, and update the robot:

```python theme={null}
CLEAR = "\033[2K\r"

def status():
    direction = moving if moving else "stopped"
    sys.stdout.write(f"{CLEAR}  [{direction:>8}]  speed={speed}%  steer={steer_angle:+d}°")
    sys.stdout.flush()

print("  Controls: W=fwd S=back A=left D=right SPACE=stop +/-=speed Q=quit\n")
status()

try:
    while True:
        ch = getch()

        if ch in ('q', 'Q', '\x03'):
            break
        elif ch in ('w', 'W'):
            moving = 'forward'
            px.forward(speed)
        elif ch in ('s', 'S'):
            moving = 'backward'
            px.backward(speed)
        elif ch in ('a', 'A'):
            steer_angle = max(steer_angle - steer_step, -35)
            px.set_dir_servo_angle(steer_angle)
        elif ch in ('d', 'D'):
            steer_angle = min(steer_angle + steer_step, 35)
            px.set_dir_servo_angle(steer_angle)
        elif ch == ' ':
            moving = None
            steer_angle = 0
            px.stop()
            px.set_dir_servo_angle(0)
        elif ch in ('+', '='):
            speed = min(speed + 10, 100)
            if moving == 'forward': px.forward(speed)
            elif moving == 'backward': px.backward(speed)
        elif ch in ('-', '_'):
            speed = max(speed - 10, 10)
            if moving == 'forward': px.forward(speed)
            elif moving == 'backward': px.backward(speed)

        status()

except KeyboardInterrupt:
    pass
finally:
    px.stop()
    px.set_dir_servo_angle(0)
    print(f"\n\n  Stopped. Bye!\n")
```

The `\033[2K\r` escape code clears the current line so the status updates in place instead of scrolling.

### Run it

```bash theme={null}
sudo python3 ~/camp/day2/keyboard_drive.py
```

| Key           | Action                           |
| ------------- | -------------------------------- |
| **W**         | Drive forward                    |
| **S**         | Drive backward                   |
| **A**         | Steer left (10° per press)       |
| **D**         | Steer right (10° per press)      |
| **SPACE**     | Stop and center steering         |
| **+** / **-** | Increase / decrease speed by 10% |
| **Q**         | Quit                             |

<Tip>
  Start at 10% speed (press **-** twice) to get a feel for the controls before increasing speed. Press **SPACE** to emergency stop at any time.
</Tip>

### Final code: keyboard\_drive.py

<Accordion title="Click to see the complete program">
  ```python theme={null}
  #!/usr/bin/env python3
  from picarx import Picarx
  import sys
  import time


  def getch():
      import tty, termios
      fd = sys.stdin.fileno()
      old = termios.tcgetattr(fd)
      try:
          tty.setraw(fd)
          ch = sys.stdin.read(1)
      finally:
          termios.tcsetattr(fd, termios.TCSADRAIN, old)
      return ch


  px = Picarx()
  speed = 30
  steer_angle = 0
  steer_step = 10
  moving = None

  CLEAR = "\033[2K\r"


  def status():
      direction = moving if moving else "stopped"
      sys.stdout.write(f"{CLEAR}  [{direction:>8}]  speed={speed}%  steer={steer_angle:+d}°")
      sys.stdout.flush()


  print("=" * 50)
  print("  PiCar-X — Keyboard Drive")
  print("=" * 50)
  print("  Controls: W=fwd S=back A=left D=right SPACE=stop +/-=speed Q=quit\n")
  status()

  try:
      while True:
          ch = getch()

          if ch in ('q', 'Q', '\x03'):
              break
          elif ch in ('w', 'W'):
              moving = 'forward'
              px.forward(speed)
          elif ch in ('s', 'S'):
              moving = 'backward'
              px.backward(speed)
          elif ch in ('a', 'A'):
              steer_angle = max(steer_angle - steer_step, -35)
              px.set_dir_servo_angle(steer_angle)
          elif ch in ('d', 'D'):
              steer_angle = min(steer_angle + steer_step, 35)
              px.set_dir_servo_angle(steer_angle)
          elif ch == ' ':
              moving = None
              steer_angle = 0
              px.stop()
              px.set_dir_servo_angle(0)
          elif ch in ('+', '='):
              speed = min(speed + 10, 100)
              if moving == 'forward': px.forward(speed)
              elif moving == 'backward': px.backward(speed)
          elif ch in ('-', '_'):
              speed = max(speed - 10, 10)
              if moving == 'forward': px.forward(speed)
              elif moving == 'backward': px.backward(speed)

          status()

  except KeyboardInterrupt:
      pass
  finally:
      px.stop()
      px.set_dir_servo_angle(0)
      print(f"\n\n  Stopped. Bye!\n")
  ```
</Accordion>

***

## Challenge: build your own sequence

Write a custom movement pattern from scratch. Try to make the robot:

* Drive in a **square** (forward, turn 90°, repeat 4 times)
* Drive in a **spiral** (gradually increase the speed while turning)
* Do a **parallel park** (forward, steer, reverse into a spot)

```bash theme={null}
nano ~/camp/day2/my_sequence.py
```

Start with `from picarx import Picarx` and `import time`, create `px = Picarx()`, and build your pattern using `forward`, `backward`, `stop`, `set_dir_servo_angle`, and `sleep`.

<Tip>
  There is no wrong answer — the goal is to understand how the commands map to physical motion.
</Tip>
