> For the complete documentation index, see [llms.txt](https://cocelo-dev.gitbook.io/support/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cocelo-dev.gitbook.io/support/flamingo-python-sdk.md).

# FlaminGO- Python SDK

## FlmainGO Python SDK

### 1. Getting Started

#### 1.1 How to Connect via SSH

When you power on the robot, a Wi-Fi hotspot is automatically enabled. You can connect to the robot through the enabled Wi-Fi.

|              |                     |
| ------------ | ------------------- |
| **SSID**     | `COCELO_ROBOT_XXXX` |
| **Password** | `00000000`          |

{% hint style="info" %}
`XXXX` is a unique 4-character alphanumeric tag for each robot.
{% endhint %}

{% code title="SSH Connection" %}

```bash
# 1) Connect to Wi-Fi (connect to the COCELO_ROBOT_XXXX network)
# 2) SSH connection
ssh cocelo@XXXX    # XXXX = same tag as the Wi-Fi SSID
                   # Password: 00000000
```

{% endcode %}

***

#### 1.2 Quick Drive Test

**Connect the Joystick**

1. Press and hold the joystick’s **center Home button** for **at least 5 seconds**.
2. Wait until pairing is complete.

{% hint style="warning" %}
If the joystick does not connect, **fully charge it to 100%** and try again.
{% endhint %}

**Motor set zero**

{% code title="Run Example" %}

```bash
cd fx_client/example
python req_test.py
```

{% endcode %}

{% hint style="danger" %}
Make sure that **emergency stop is not pressed.**
{% endhint %}

* Press `a` in the terminal to get the motor state. Make sure all the state is set to "2" after pressing `a` then go to the next step.
* Press `z` in the terminal to send all motor to zero position.
* Robot will go the the standing position after pressing `z`.
* Press `q` in the terminal to quit the motor set-zero mode.

{% hint style="danger" %}
**Make sure to press `q` and go to the Wake/Control step.**&#x20;
{% endhint %}

**Wake/Control the Robot**

{% code title="Run Example" %}

```bash
cocelo run example
```

{% endcode %}

* Use the **controller’s left stick** to control the robot’s movement.
* **Exit**: `Ctrl + C`

{% hint style="danger" %}
When you exit, the motors stop immediately. Be sure to **hold the handle** and support the robot before exiting.
{% endhint %}

***

#### 1.3 View Execution Logs

| Command                      | Description                      |
| ---------------------------- | -------------------------------- |
| `cocelo log list`            | View the list of saved log files |
| `cocelo log show last`       | View the most recent log         |
| `cocelo log show <filename>` | View a specific log file         |

{% code title="Log Command Examples" %}

```bash
cocelo log list
cocelo log show last
cocelo log show <filename>
```

{% endcode %}

***

### 2. SDK Preview

The example below shows the core SDK usage flow.

{% code title="play.py" %}

```python
from flamingo_sdk import *

# 1) Robot gain settings
kp = [70.0, 70.0, 70.0, 70.0, 70.0, 70.0, 0.0, 0.0]
kd = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.7, 0.7]

# 2) Define RL mode (policy and observation settings)
mode = Mode(mode_cfg={
    "id": 1,
    "stacked_obs_order": ["dof_pos", "dof_vel", "ang_vel", "proj_grav", "last_action"],
    "non_stacked_obs_order": ["command"],
    "obs_scale": {
        "dof_vel": 0.05,
        "ang_vel": 0.25,
        "command": [2.0, 0.0, 0.25, 0.0],
    },
    "action_scale": [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 40.0, 40.0],
    "stack_size": 3,
    "policy_path": "your_policy.onnx",
    "cmd_vector_length": 4,
})

# 3) Create instances
robot = Robot()
mapping = [
    "LEFT_Y",                   # cmd[0]
    "BTN_THUMBL",               # cmd[1]
    "LEFT_X",                   # cmd[2]
    "BTN_A:-0.4,BTN_Y:0.4",     # cmd[3]
]
joystick = Joystick(mapping=mapping, max_cmd=[(-1.0, 1.0), (-0.0, 0.0), (-1.5, 1.5), (-0.0, 0.0)])
rl = RL()

# 4) Initialize
robot.set_gains(kp=kp, kd=kd)
rl.add_mode(mode)
rl.set_mode(mode_id=1)

# 5) Control loop (50Hz)
@control_rate(robot, hz=50)
def loop():
    obs = robot.get_obs()              # Acquire sensor data
    cmd = joystick.get_cmd()           # Acquire joystick commands
    state = rl.build_state(obs, cmd)   # Build state for inference
    action = rl.select_action(state)   # Policy inference
    robot.do_action(action)            # Send motor commands

loop()
```

{% endcode %}

{% hint style="info" %}
Typical control loop order: `get_obs()` → `get_cmd()` → `build_state()` → `select_action()` → `do_action()`
{% endhint %}

***

### 3. Package Structure

#### Core Components

| Component      | Role                                                              |
| -------------- | ----------------------------------------------------------------- |
| `Robot`        | Robot interface                                                   |
| `Joystick`     | Provides joystick input as `cmd`                                  |
| `Mode`         | Policy (`.onnx`)+ observation/scale/stack settings                |
| `RL`           | Builds network input `state` from `obs` + `cmd` + selects actions |
| `control_rate` | Runs the control loop at a specified rate                         |

***

### 4. API Reference

### Package Contents

#### `flamingo_sdk`

* `Robot`
* `Joystick`
* `Mode`
* `ModeConfig`
* `RL`
* `control_rate`

#### `flamingo_sdk.onnxpolicy`

* `MLPPolicy`
* `LSTMPolicy`

#### Exceptions

* `RobotInitError`
* `RobotSetGainsError`
* `RobotEStopError`
* `ModeConfigError`

***

### `flamingo_sdk`

#### class `flamingo_sdk.Robot()`

{% hint style="success" %}

```python
class Robot()
```

Controls the hardware interface. It reads sensor observations and sends motor commands.
{% endhint %}

{% tabs %}
{% tab title="Quickstart" %}
{% code title="Basic Robot Usage" %}

```python
from flamingo_sdk import *

robot = Robot()                       # Create a Robot instance
robot.set_gains(kp=[...], kd=[...])   # Set PD gains
obs = robot.get_obs()                 # Acquire sensor data
robot.do_action([0, 0, 0, 0, 0, 0, 0, 0])         # Send motor commands
robot.estop()                         # Emergency stop
```

{% endcode %}
{% endtab %}

{% tab title="Joint Indices" %}

|         Index | Joint           |
| ------------: | --------------- |
|             0 | left\_shoulder  |
|             1 | right\_shoulder |
|             2 | left\_wheel     |
|             3 | right\_wheel    |
|  {% endtab %} |                 |
| {% endtabs %} |                 |

**Attributes**

**`gains_set: bool`**

Whether gains have been set.

***

**`Robot()`**

Creates a robot instance.

**Raises**

* `RobotInitError`: Initialization failed

***

**`set_gains(kp: list[float], kd: list[float]) -> None`**

Sets PD gains for the 8 motors (joints).

**Parameters**

* `kp (list[float])`: Proportional gains (length 4)
* `kd (list[float])`: Derivative gains (length 4)

{% code title="set\_gains Example" %}

```python
#set_gains example
#Order: left hip, right hip, left shoulder, right shoulder, left_leg, right_leg, left_wheel, right_wheel

kp = [70.0, 70.0, 70.0, 70.0, 70.0, 70.0, 0.0, 0.0]
kd = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.7, 0.7]

robot.set_gains(kp=kp, kd=kd)
```

{% endcode %}

{% hint style="info" %}
The motor order corresponding to the gains in the `kp`/`kd` lists must match the following exactly.

&#x20;`Index 0`: Left hip motor

&#x20;`Index 1`: Right hip  motor

&#x20;`Index 2`: Left shoulder motor

&#x20;`Index 3`: Right shoulder motor

&#x20;`Index 4`: Left leg motor

&#x20;`Index 5`: Right leg motor

&#x20;`Index 6`: Left wheel motor

&#x20;`Index 7`: Right wheel motor
{% endhint %}

**Raises**

* `RobotSetGainsError`

***

**`get_obs() -> dict`**

Returns the current sensor data.

{% code title="get\_obs Example" %}

```python
obs = robot.get_obs()
print(obs)
# Example: {"dof_pos": [...], "dof_vel": [...], "ang_vel": [...], "proj_grav": [...]}
```

{% endcode %}

***

**`do_action(action: list[float], torque_ctrl: bool = False) -> None`**

Sends motor commands.

**Parameters**

* `action (list[float])`: Target values (or torque values) for 4 motors
* `torque_ctrl (bool, optional)`: If `True`, torque control mode

{% tabs %}
{% tab title="Basic Control" %}
{% code title="Position (shoulder) + Velocity (wheel) Control" %}

```python
# Order: left hip, right hip, left shoulder, right shoulder, left leg, right leg, left wheel, right wheel
robot.do_action([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8])
```

{% endcode %}
{% endtab %}

{% tab title="Torque Control" %}
{% code title="torque\_ctrl=True" %}

```python
robot.do_action([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], torque_ctrl=True)
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
Please make sure to follow the argument order for the `do_action` method.

`Index 0`: Left joint motor `position (rad)` control&#x20;

`Index 1`: Right joint motor `position (rad)` control&#x20;

`Index 2`: Left joint motor `position (rad)` control&#x20;

`Index 3`: Right joint motor `position (rad)` control&#x20;

`Index 4`: Left joint motor `position (rad)` control&#x20;

`Index 5`: Right joint motor `position (rad)` control&#x20;

`Index 6`: Left wheel motor `velocity (rad/s)` control&#x20;

`Index 7`: Right wheel motor `velocity (rad/s)` control

\*When `torque_ctrl`=`True`, torque control is used for those motors
{% endhint %}

***

**`estop(msg: str = "", verbose: bool = True) -> None`**

Executes an emergency stop.

{% code title="estop Example" %}

```python
robot.estop("Emergency")
```

{% endcode %}

**Raises**

* `RobotEStopError`

***

**`get_gains() -> dict`**

Returns the current gain settings.

{% code title="get\_gains Example" %}

```python
gains = robot.get_gains()
print(gains)  # {"kp": [...], "kd": [...]}
```

{% endcode %}

***

#### class `flamingo_sdk.RL()`

{% hint style="success" %}

```python
class RL()
```

Manages the reinforcement learning policy, builds the neural network input `state` from `obs` and `cmd`, and selects actions.
{% endhint %}

{% tabs %}
{% tab title="Quickstart" %}
{% code title="Basic RL Usage" %}

```python
from flamingo_sdk import *

rl = RL()
rl.add_mode(mode)                     # Add a mode
rl.set_mode(mode_id=1)                # Set the active mode
state = rl.build_state(obs, cmd)      # Build state
action = rl.select_action(state)      # Select action
```

{% endcode %}
{% endtab %}

{% tab title="Flow" %}
`build_state()` creates the input vector according to the mode settings (stack/scale/observation order), and `select_action()` runs policy inference.
{% endtab %}
{% endtabs %}

***

**`add_mode(mode: Mode) -> None`**

Adds a mode. (Supports multiple modes)

***

**`set_mode(mode_id: int | None = None) -> None`**

Sets the mode to activate.

**Parameters**

* `mode_id (int)`: Mode ID

***

**`build_state(obs: dict, cmd: dict, last_action: list[float] | None = None) -> list[float]`**

Converts observations into the neural network input (state) format.

**Parameters**

* `obs (dict)`: Return value of `robot.get_obs()`
* `cmd (dict)`: Return value of `joystick.get_cmd()`
* `last_action (list[float] | None)`: User-provided last\_action clipped to \[-1,1] (optional)

{% hint style="info" %}
If you do not pass `last_action` as an argument, RL stores and uses last\_action internally (recommended).
{% endhint %}

{% code title="build\_state Example" %}

```python
state = rl.build_state(obs, cmd)
state = rl.build_state(obs, cmd, last_action=[0, 0, 0, 0, 0, 0, 0, 0])  # Specify manually if needed
```

{% endcode %}

**Returns**

* `list[float]`: 1D state vector

***

**`select_action(state: list[float]) -> list[float]`**

Runs the policy to generate an action.

{% code title="select\_action Example" %}

```python
action = rl.select_action(state)
```

{% endcode %}

***

#### class `flamingo_sdk.Mode(mode_cfg: dict)`

{% hint style="success" %}

```python
class Mode(mode_cfg: dict)
```

Defines per-mode policy settings. (Includes observation stack/scale and policy file path)
{% endhint %}

{% tabs %}
{% tab title="Basic Example" %}
{% code title="Mode Configuration Example" %}

```python
mode = Mode(mode_cfg={
    "id": 1,
    "stacked_obs_order": ["dof_pos", "dof_vel", "ang_vel"],
    "non_stacked_obs_order": ["command"],
    "obs_scale": {"dof_vel": 0.05, "command": 2.0},
    "action_scale": [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 40.0, 40.0],
    "stack_size": 3,
    "policy_path": "weights/model.onnx",
    "cmd_vector_length": 4,
})
```

{% endcode %}
{% endtab %}

{% tab title="state Structure" %}
The `build_state()` result has the following form.

```
[ stacked_obs (newest → oldest) | non_stacked_obs ]
```

Example: `stack_size=3`, `stacked_obs_order=["dof_pos","dof_vel"]`, `non_stacked_obs_order=["command"]`

```
[ dof_pos_t | dof_vel_t | dof_pos_t-1 | dof_vel_t-1 | dof_pos_t-2 | dof_vel_t-2 | command ]
```

{% endtab %}
{% endtabs %}

**`mode_cfg` keys**

| Key                     | Type                   |    Default | Description                                       |
| ----------------------- | ---------------------- | ---------: | ------------------------------------------------- |
| `id`                    | `int`                  | (Required) | Mode ID (1\~16)                                   |
| `policy_path`           | `str`                  | (Required) | Path to the ONNX policy file                      |
| `stacked_obs_order`     | `list[str]`            |       `[]` | List of observation keys to stack                 |
| `non_stacked_obs_order` | `list[str]`            |       `[]` | List of observation keys not to stack             |
| `obs_scale`             | `dict`                 |       `{}` | Per-observation scale (scalar or vector)          |
| `action_scale`          | `float \| list[float]` |     `0.25` | Action scale                                      |
| `stack_size`            | `int`                  |        `3` | Number of stacked frames                          |
| `policy_type`           | `str`                  |    `"mlp"` | `"mlp"` / `"lstm"`                                |
| `cmd_vector_length`     | `int`                  |        `0` | Command vector dimension                          |
| `extra_obs`             | `dict[str,int]`        |       `{}` | Custom observations (key: name, value: dimension) |

***

**Default Observations**

| Key           | Description                             |           Dimension |
| ------------- | --------------------------------------- | ------------------: |
| `dof_pos`     | Motor angle (rad)                       |                   2 |
| `dof_vel`     | Motor velocity (rad/s)                  |                   4 |
| `ang_vel`     | Angular velocity `(wx, wy, wz)` (rad/s) |                   3 |
| `proj_grav`   | Projected gravity vector `(gx, gy, gz)` |                   3 |
| `last_action` | Previous-step action output             |                   4 |
| `command`     | Joystick command vector                 | `cmd_vector_length` |

***

**Attributes (read-only)**

* `id: int`
* `stack_size: int`
* `state_length: int`
* `action_length: int`
* `policy_type: str`
* `policy_path: str`
* `stacked_obs_order: list[str]`
* `non_stacked_obs_order: list[str]`
* `action_scale: list[float]`
* `obs_scale: dict`
* `obs_lengths: dict`
* `cmd_vector_length: int`

***

**`inference(state: list[float]) -> list[float]`**

Directly performs ONNX policy inference.

{% code title="inference Example" %}

```python
action = mode.inference(state)
```

{% endcode %}

***

#### class `flamingo_sdk.Joystick(max_cmd: list[pair(double, double)]=[], smoothness: float=50.0, mapping: dict={})`

{% hint style="success" %}

```python
class Joystick(max_cmd=[], smoothness=50.0, mapping={})
```

Processes joystick input and returns `cmd`.
{% endhint %}

{% tabs %}
{% tab title="Quickstart" %}
{% code title="Basic Joystick Usage" %}

```python
joystick = Joystick()
cmd = joystick.get_cmd()
```

{% endcode %}
{% endtab %}

{% tab title="How to Connect" %}
**Connect to the robot:** Press and hold the Home button for at least 5 seconds.
{% endtab %}
{% endtabs %}

**Parameters**

* `max_cmd (list[pair(double, double)], optional)`
  * Limits each axis output range to `[min_cmd[i], max_cmd[i]]`.
  * If the argument list length is shorter than `cmd_vector_length`, the remaining values are filled with `(-1.0, 1.0)`.
  * To disable a specific axis, set the corresponding index to `(-0.0, 0.0)`.

{% code title="max\_cmd Example (Use Only Some Axes)" %}

```python
# Use only indices 0 and 2; disable the rest
joystick = Joystick(max_cmd=[(-1.0, 1.0), (-0.0, 0.0), (-0.5, 0.5), (-0.0, 0.0)])
# → cmd_vector[0]: -1.0 ~ +1.0
# → cmd_vector[1]: always 0
# → cmd_vector[2]: -0.5 ~ +0.5
# → cmd_vector[3]: always 0
# → cmd_vector[4], [5]: not use // length of cmd_vector is 4
```

{% endcode %}

* `smoothness (float, optional)`\
  Applies a filter to joystick input. The larger the value, the smoother the output, but the slower the input response.

{% tabs %}
{% tab title="Fast Response" %}
{% code title="smoothness=10" %}

```python
joystick = Joystick(max_cmd=[(-1.0, 1.0), (0.0, 0.0), (-1.0, 1.0), (0.0, 0.0)], smoothness=10.0)
```

{% endcode %}
{% endtab %}

{% tab title="Smooth Movement" %}
{% code title="smoothness=80" %}

```python
joystick = Joystick(max_cmd=[(-1.0, 1.0), (0.0, 0.0), (-1.0, 1.0), (0.0, 0.0)], smoothness=80.0)
```

{% endcode %}
{% endtab %}
{% endtabs %}

* `mapping(str)` : Maps buttons to the command vector in order. For discrete buttons, the delta value can also be specified.

{% code title="Mapping Example" %}

```python
custom_mapping = {
    "LEFT_Y",                   # cmd[0]
    "BTN_THUMBL",               # cmd[1]
    "LEFT_X",                   # cmd[2]
    "BTN_A:-0.5,BTN_Y:0.4",     # cmd[3], amount of change must be specified(decrease -0.5, increase 0.4)
}

joystick = Joystick(mapping=custom_mapping)
```

{% endcode %}

**Mapping Rules**

* Users must map the same number of buttons as the `cmd_vector_length` defined in the mode.
* Each button must be uniquely mapped, and for discrete buttons such as `BTN_A`, `BTN_B`, `BTN_Y`, and `BTN_X`, both increase and decrease deltas must be defined.

***

**Mode Switching (mode\_id)**

Select `mode_id(1~16)` using a combination of the D-pad (⬆️➡️⬇️⬅️) and the face buttons (Y/B/A/X) combination.\
**How to operate:** While holding a D-pad direction, press one of Y/B/A/X at the same time.

| D-pad Direction |  Y  |  B  |  A  |  X  |
| :-------------: | :-: | :-: | :-: | :-: |
|      ⬆️ Up      |  1  |  2  |  3  |  4  |
|     ➡️ Right    |  5  |  6  |  7  |  8  |
|     ⬇️ Down     |  9  |  10 |  11 |  12 |
|     ⬅️ Left     |  13 |  14 |  15 |  16 |

***

**Emergency Stop (E-Stop)**

| Function                    | Input Method                         |
| --------------------------- | ------------------------------------ |
| **Emergency Stop (E-Stop)** | Press LT + RT buttons simultaneously |

{% hint style="danger" %}
During an emergency stop (E-Stop), all motors stop immediately.
{% endhint %}

***

**`get_cmd() -> dict`**

Returns joystick commands and status.

**Returns**

* `cmd_vector (list[float])`: Command vector
* `mode_id (int | None)`: Current mode ID
* `estop (bool)`: Whether E-Stop input is detected

{% code title="get\_cmd Return Example" %}

```python
cmd = joystick.get_cmd()
# {"cmd_vector": [...], "mode_id": 1, "estop": False}
```

{% endcode %}

***

**`is_connected() -> bool`**

Checks the joystick connection status.

{% code title="is\_connected Example" %}

```python
if joystick.is_connected():
    print("Connected")
```

{% endcode %}

***

#### function `flamingo_sdk.control_rate(robot: Robot, hz: float = 50.0)`

{% hint style="info" %}
A decorator that runs the control loop at the specified Hz.
{% endhint %}

{% code title="control\_rate Example" %}

```python
@control_rate(robot, hz=50)
def loop():
    # Control logic
    pass

loop()
```

{% endcode %}

***

### `flamingo_sdk.onnxpolicy`

#### class `flamingo_sdk.onnxpolicy.MLPPolicy(weightPath: str)`

{% hint style="success" %}

```python
class MLPPolicy(weightPath: str)
```

An ONNX-based MLP policy inference engine.
{% endhint %}

**Parameters**

* `weightPath (str)`: Path to the ONNX model file

**Methods**

**`inference(state: list[float]) -> list[float]`**

Performs 1-step inference. `state` **must be 1D**.

***

#### class `flamingo_sdk.onnxpolicy.LSTMPolicy(weightPath: str)`

{% hint style="success" %}

```python
class LSTMPolicy(weightPath: str)
```

An ONNX-based LSTM policy (with state retention) inference engine.
{% endhint %}

**Parameters**

* `weightPath (str)`: Path to the ONNX model file

**Methods**

**`inference(state: list[float]) -> list[float]`**

Performs 1-step inference (including internal h/c updates). `state` **must be 1D**.

***

### 5. Example Code

{% tabs %}
{% tab title="Example 1: Observation Check Test" %}
{% code title="example1.py" %}

```python
from flamingo_sdk import *

# Robot's Gains
kp = [70.0, 70.0, 70.0, 70.0, 70.0, 70.0, 0.0, 0.0]
kd = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.7, 0.7]

# Instances
robot = Robot()

# Set gains
robot.set_gains(kp=kp, kd=kd)

# Get and print observation
obs = robot.get_obs()
print(obs)

# Send zero action
action = [0, 0, 0, 0, 0, 0, 0, 0]
robot.do_action(action)
```

{% endcode %}
{% endtab %}

{% tab title="Example 2: Joystick Test" %}
{% code title="example2.py" %}

```python
from flamingo_light_sdk import *

# Robot's Gains
kp = [15, 15, 0, 0]
kd = [0.45, 0.45, 0.3, 0.3]

# Instances
robot = Robot()
joystick = Joystick()

# Set gains
robot.set_gains(kp=kp, kd=kd)

# Get observation and command
obs = robot.get_obs()
cmd = joystick.get_cmd()
print(cmd)

# Send zero action
action = [0, 0, 0, 0]
robot.do_action(action)
```

{% endcode %}
{% endtab %}

{% tab title="Example 3: Control Using the RL Class" %}
{% code title="example3.py" %}

```python
from flamingo_light_sdk import *

# Robot's Gains
kp = [15, 15, 0, 0]
kd = [0.45, 0.45, 0.3, 0.3]

# RL mode
mode = Mode(mode_cfg={
    "id": 1,
    "stacked_obs_order": ["dof_pos", "dof_vel", "ang_vel", "proj_grav", "last_action"],
    "non_stacked_obs_order": ["command"],
    "obs_scale": {"dof_vel": 0.15,
                  "ang_vel": 0.25,
                  "command": [2.0, 0.0, 0.25, 0.0],
                  },
    "action_scale": [0.85, 0.85, 40, 40],
    "stack_size": 3,
    "policy_path": "weights/drive.onnx",
    "cmd_vector_length": 4,
})

# Instances
robot = Robot()
joystick = Joystick(max_cmd=[1, 0, 1, 0])
rl = RL()

# Set gains
robot.set_gains(kp=kp, kd=kd)

# Add & Set Mode
rl.add_mode(mode)
rl.set_mode(mode_id=1)


@control_rate(robot, hz=50)
def loop():
    obs = robot.get_obs()              # Get observation
    cmd = joystick.get_cmd()           # Get command
    state = rl.build_state(obs, cmd)   # Build state (RL helper)
    action = rl.select_action(state)   # Select action
    robot.do_action(action)            # Execute action

loop()
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

### 6. Applications

#### 6.1 Switching Between Multiple Modes

Switching between multiple policies is supported. If `build_state()` detects `mode_id` in `cmd`, it can automatically switch modes.

{% code title="multi\_mode.py (Conceptual Example)" %}

```python
from flamingo_sdk import *

walk_mode = Mode(mode_cfg={"id": 1, "policy_path": "weights/walk.onnx", ...})
run_mode  = Mode(mode_cfg={"id": 2, "policy_path": "weights/run.onnx",  ...})

rl = RL()
rl.add_mode(walk_mode)
rl.add_mode(run_mode)
rl.set_mode(mode_id=1)  # Set initial mode

@control_rate(robot, hz=50)
def loop():
    obs = robot.get_obs()
    cmd = joystick.get_cmd()

    # Automatically switch modes when mode_id changes in cmd
    state = rl.build_state(obs, cmd)
    action = rl.select_action(state)
    robot.do_action(action)

loop()
```

{% endcode %}

***

#### 6.2 Customizing Observations

You can process sensor data to create new observations and add them to the policy input. Define the dimensions of custom observations in advance with `extra_obs`, then add the data to the `obs` dictionary inside the loop.

{% code title="example4.py (Conceptual Example)" %}

```python
from flamingo_sdk import *

# Robot's Gains
kp = [70.0, 70.0, 70.0, 70.0, 70.0, 70.0, 0.0, 0.0]
kd = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.7, 0.7]

# RL mode with extra observations
mode = Mode(mode_cfg={
    "id": 1,
    "extra_obs": {
        "extra1": 4,   # 4D custom observation
        "extra2": 8,   # 8D custom observation
    },
    "stacked_obs_order": ["dof_pos", "dof_vel", "ang_vel", "proj_grav", "last_action", "extra1"],
    "non_stacked_obs_order": ["extra2"],
    "obs_scale": {"dof_vel": 0.05,
                  "ang_vel": 0.25,
                  },
    "action_scale": [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 40.0, 40.0],
    "stack_size": 3,
    "policy_path": "your_policy.onnx",
    "cmd_vector_length": 0,  # Do not use joystick commands
})

# Instances
robot = Robot()
mapping = [
    "LEFT_Y",                   # cmd[0]
    "BTN_THUMBL",               # cmd[1]
    "LEFT_X",                   # cmd[2]
    "BTN_A:-0.4,BTN_Y:0.4",     # cmd[3]
]
joystick = Joystick(mapping=mapping, max_cmd=[(-1.0, 1.0), (-0.0, 0.0), (-1.5, 1.5), (-0.0, 0.0)])
rl = RL()

# Set gains
robot.set_gains(kp=kp, kd=kd)

# Add & Set Mode
rl.add_mode(mode)
rl.set_mode(mode_id=1)


@control_rate(robot, hz=50)
def loop():
    obs = robot.get_obs()                   # Get robot observation
    obs['extra1'] = GetCustomObs()          # Add extra observation 1 (1D list or np.ndarray)
    obs['extra2'] = GetLatentVector()       # Add extra observation 2 (1D list or np.ndarray)

    cmd = {"cmd_vector": None, "mode_id": None, "estop": False}
    state = rl.build_state(obs, cmd)        # Build state with extra obs
    action = rl.select_action(state)        # Select action
    robot.do_action(action)                 # Execute action

loop()
```

{% endcode %}

{% hint style="warning" %}
Extra observations must be in the form of a **1D list** or a **1D np.ndarray**.
{% endhint %}

***

#### 6.3 Torque Control

Use torque control mode instead of position/velocity control.

{% code title="torque\_ctrl.py" %}

```python
@control_rate(robot, hz=50)
def loop():
    obs = robot.get_obs()
    cmd = joystick.get_cmd()
    state = rl.build_state(obs, cmd)
    action = rl.select_action(state)
    robot.do_action(action, torque_ctrl=True)  # Torque control mode
```

{% endcode %}

***

#### 6.4 Scale Settings

How to apply scales to observations and actions.

{% tabs %}
{% tab title="Single Scalar" %}
Applies the same scale to all elements.

```python
"obs_scale": {"dof_vel": 0.05}  # All 4 elements × 0.05
```

{% endtab %}

{% tab title="Vector Scale" %}
Applies per-element scales. The vector length must match the dimension of the corresponding observation.

```python
"obs_scale": {"dof_vel": [0.1, 0.15, 1, 1]}  # dof_vel is 4D, so length 4
```

```python
"obs_scale": {"command": [2.0, 0.0, 0.25, 0.0]}  # When cmd_vector_length=4
```

{% endtab %}
{% endtabs %}

***

### 7. SDK Update

This requires an internet connection. After connecting via SSH, proceed in the following order.

{% code title="Update" %}

```bash
# 1. Enable Wi-Fi
sudo nmcli radio wifi on

# 2. Scan nearby networks
sudo nmcli dev wifi list

# 3. Connect to a network
sudo nmcli dev wifi connect "<SSID>" password "<PASSWORD>"

# 4. Update to the latest SDK
pip install -U flamingo-light-sdk

# 5. Disable Wi-Fi
sudo nmcli radio wifi off
```

{% endcode %}

***
