Skip to content

Commit 2796ac2

Browse files
committed
More payload options available
1 parent c90941a commit 2796ac2

10 files changed

Lines changed: 332 additions & 301 deletions

File tree

README.md

Lines changed: 29 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,57 @@
11

22
# OpenTouch Interface
33

4-
The OpenTouch Interface is a Python package designed to provide a unified interface for various touch sensors. It simplifies the process of interacting with touch sensors by providing a consistent API regardless of the specific sensor being used.
4+
A Python package designed to provide a unified interface for various touch sensors. It simplifies the process of interacting with touch sensors by providing a consistent API regardless of the specific sensor being used.
55

66
We also provide an easy-to-use web interface that lets you connect to the sensors and show their data stream without the need to code.
77

88
---
99

10-
## Features
10+
## Overview
11+
- **Unified API**: Interact with multiple types of touch sensors using a single, consistent API.
12+
- **Web Dashboard**: Manage sensors, record/replay data, and annotate data directly through a web interface.
13+
- **Easy Configuration**: Use YAML config files to quickly set up and manage sensors.
1114

12-
We currently offer a unified API along with a simple web interface for the following touch sensors:
15+
**Supported sensors**:
1316
- [Digit](https://digit.ml/)
1417
- [Digit360](https://digit.ml/digit360.html)
1518
- [GelSight Mini](https://www.gelsight.com/gelsightmini/)
1619

17-
Key features include:
18-
19-
- **Unified API**: Interact with multiple types of touch sensors using a single, consistent API.
20-
- **Web Dashboard**: Manage sensors, record and replay data, and annotate data directly through a web interface.
21-
- **Easy Configuration**: Use YAML config files to quickly set up and manage sensors.
22-
2320
## Installation
2421

25-
OpenTouch Interface requires Python 3.10 or higher and has been tested on Ubuntu 24.04.
26-
You can install it in one of two ways: either via `pip` or by cloning the repository.
22+
OpenTouch Interface requires Python 3.10+ (tested on Ubuntu 24.04).
2723

28-
### Method 1: Install via `pip`
24+
#### Method 1: Install via `pip`
2925

3026
```bash
3127
pip install opentouch-interface
3228
```
3329

34-
### Method 2: Install via Cloning the Repository
30+
#### Method 2: From source
3531

3632
```bash
3733
git clone https://github.com/lasr-lab/opentouch-interface
3834
cd opentouch-interface
3935
pip install .
4036
```
4137

42-
## Connecting Sensors
38+
## Getting started
4339

4440
You can connect touch sensors to the OpenTouch Interface in two main ways:
4541

46-
### 1. Dashboard (Web Interface)
42+
### 1. Using the Dashboard (Web Interface)
4743

48-
The dashboard provides an intuitive web-based interface. To start the dashboard, run:
44+
<p align="center">
45+
<img src="./docs/dashboard.gif" alt="dashboard" />
46+
</p>
4947

48+
Start the dashboard. If you run into issues, try reconnecting your sensors and restarting the dashboard.
5049
```bash
5150
opentouch-dashboard
5251
```
5352

54-
If you encounter any errors, try reconnecting your sensors and restarting the dashboard.
55-
5653
#### Adding Sensors to the Dashboard
57-
58-
You can add sensors to the dashboard by:
59-
60-
1. **Manual Entry**: Enter sensor details directly in the dashboard.
61-
2. **YAML Configuration**: Use a YAML config file to define sensor groups and settings.
62-
63-
Sensors are bundled in groups. Within a group **sensor names must be unique**.
64-
An example config file is provided as [`group.yaml`](examples/config/group.yaml).
65-
It is structured as follows:
54+
Add sensors either by manually entering details or by providing a YAML config file. For example, your YAML might look like:
6655
```yaml
6756
group_name: Robotic hand # Group name.
6857
path: test.touch # File path where data should be saved (optional).
@@ -75,45 +64,27 @@ payload: # List of input elements used for data annotations (optional).
7564
- ... # e.g., text input
7665
- ... # e.g., slider
7766
```
67+
Sensors are bundled in groups. Within a group **sensor names must be unique**.
68+
An example config file is provided as [`group.yaml`](examples/config/group.yaml). There, you also find example configs of all supported sensors.
7869

79-
There, you also find example configs of all supported sensors.
80-
81-
The payload can be used for data annotations. You can use the following elements:
82-
- [Slider](https://docs.streamlit.io/develop/api-reference/widgets/st.slider)
83-
```yaml
84-
type: slider # Use 'slider'.
85-
label: Difficulty # Label that will be displayed to the user.
86-
min_value: -10 # Minimum value (int). Default: 0.
87-
max_value: 20 # Maximum value (int). Default: 10.
88-
default: 8 # Default value (int). Default: 0.
89-
```
90-
91-
- [Text input](https://docs.streamlit.io/develop/api-reference/widgets/st.text_input)
92-
```yaml
93-
type: text_input # Use 'text_input'.
94-
label: Grasped object # Label that will be displayed to the user.
95-
default: apple # Default value (str). Default: "".
96-
```
70+
The payload can be used for data annotations. You can find an overview of supported widgets at [`payload.md`](examples/payload.md).
9771

9872
### 2. Using Code
9973

100-
For users who prefer a programmatic approach, the OpenTouch Interface also allows direct interaction through code.
101-
102-
Please have a look at our [`demo.py`](examples/demo.py) to see how to use it.
74+
For programmatic control, please refer to the [`demo.py`](examples/demo.py) example.
75+
- Run with the default config ([`digit.yaml`](examples/config/digit.yaml))
76+
```bash
77+
python examples/simple/demo.py
78+
```
10379

104-
```bash
105-
python examples/simple/demo.py
106-
```
107-
108-
By default, this script uses the [`digit.yaml`](examples/config/digit.yaml) config file. You can specify a different config file via the terminal:
109-
110-
```bash
111-
python examples/simple/demo.py --config-name gelsight.yaml
112-
```
80+
- Or specify a config file:
81+
```bash
82+
python examples/simple/demo.py --config-name gelsight.yaml
83+
```
11384
---
11485

11586
## Contribution
116-
If you want to contribute to OpenTouch Interface, you can find an explanation on how to do so [here](CONTRIBUTING.md).
87+
We welcome contributions! Pleaes check out our [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
11788

11889
---
11990

examples/payload.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
This document contains a list of all Streamlit widgets the OpenTouch Interface supports for payload.
2+
Each widget's parameters are mandatory. The key `default` is the value of the widget when it first renders.
3+
It is equivalent to what the Streamlit documentation usually refers to as the `value`, `index` or `default`.
4+
5+
#### [Slider](https://docs.streamlit.io/develop/api-reference/widgets/st.slider)
6+
```yaml
7+
type: slider # Widget type, always 'slider'.
8+
label: Select Age # Description displayed to the user, must be unique.
9+
min_value: 0 # Minimum value (float). Default: 0.
10+
max_value: 100 # Maximum value (float). Default: 10.
11+
step: 1 # Step size (float). Default: 1.
12+
default: 25 # Initial slider value (float). Default: min_value.
13+
```
14+
15+
#### [Text Input](https://docs.streamlit.io/develop/api-reference/widgets/st.text_input)
16+
```yaml
17+
type: text_input # Widget type, always 'text_input'.
18+
label: Enter Your Name # Description displayed to the user, must be unique.
19+
default: "John Doe" # Initial text input value (str). Default: "".
20+
```
21+
22+
#### [Checkbox](https://docs.streamlit.io/develop/api-reference/widgets/st.checkbox)
23+
```yaml
24+
type: checkbox # Widget type, always 'checkbox'.
25+
label: Accept Terms and Conditions # Description displayed to the user, must be unique.
26+
default: false # Initial checkbox state (bool). Default: False.
27+
```
28+
29+
#### [Multiselect](https://docs.streamlit.io/develop/api-reference/widgets/st.multiselect)
30+
```yaml
31+
type: multiselect # Widget type, always 'multiselect'.
32+
label: Choose Your Hobbies # Description displayed to the user, must be unique.
33+
options: ['Reading', 'Traveling', 'Cooking'] # List of available options (list of str). Default: [].
34+
default: ['Reading', 'Cooking'] # Default selected values; subset of options (list of str). Default: empty list
35+
```
36+
37+
#### [Radio](https://docs.streamlit.io/develop/api-reference/widgets/st.radio)
38+
```yaml
39+
type: radio # Widget type, always 'radio'.
40+
label: Select Your Gender # Description displayed to the user, must be unique.
41+
options: ['Male', 'Female', 'Other'] # List of available options (list of str). Default: [].
42+
default: 'Male' # Default value (str). Defaults to the first element in options.
43+
```
44+
45+
#### [Selectbox](https://docs.streamlit.io/develop/api-reference/widgets/st.selectbox)
46+
```yaml
47+
type: selectbox # Widget type, always 'selectbox'.
48+
label: Choose Your Country # Description displayed to the user, must be unique.
49+
options: ['USA', 'Canada', 'UK'] # List of available options (list of str). Default: [].
50+
default: 'USA' # Default value (str). Defaults to the first element in options or None if options are empty.
51+
```
52+
53+
#### [Number Input](https://docs.streamlit.io/develop/api-reference/widgets/st.number_input)
54+
```yaml
55+
type: number_input # Widget type, always 'number_input'.
56+
label: Enter Your Height (cm) # Description displayed to the user, must be unique.
57+
min_value: 50 # Minimum value (float). Default: 0.
58+
max_value: 250 # Maximum value (float). Default: 100.
59+
step: 1 # Step size (float). Default: 1.
60+
default: 170 # Default value (float). Defaults to min_value.
61+
```

opentouch_interface/core/registries/class_registries.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ def get_config(cls, name: str):
7373
def get_configs(cls):
7474
return cls._widgets
7575

76+
@classmethod
77+
def get_widget_names(cls):
78+
return list(cls._widgets.keys())
79+
7680

7781
class ViewerClassRegistry:
7882
_viewers = {}

opentouch_interface/core/validation/streamlit/slider_config.py

Lines changed: 0 additions & 25 deletions
This file was deleted.

opentouch_interface/core/validation/streamlit/text_input_config.py

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
from pydantic import BaseModel, Field, model_validator
2+
from opentouch_interface.core.registries.class_registries import WidgetConfigRegistry
3+
4+
5+
@WidgetConfigRegistry.register_widget('slider')
6+
class SliderConfig(BaseModel):
7+
"""
8+
Slider widget configuration.
9+
"""
10+
type: str = Field(default='slider', description='Widget type')
11+
label: str = Field(description='Short description')
12+
min_value: float = Field(default=0, description='Defaults to 0')
13+
max_value: float = Field(default=10, description='Defaults to 10')
14+
step: float = Field(default=1, description='Defaults to 1')
15+
default: float = Field(default=0, description='Defaults to 0')
16+
17+
@model_validator(mode='after')
18+
def validate_model(self):
19+
if self.default < self.min_value:
20+
self.default = self.min_value
21+
elif self.default > self.max_value:
22+
self.default = self.max_value
23+
return self
24+
25+
26+
@WidgetConfigRegistry.register_widget('text_input')
27+
class TextInputConfig(BaseModel):
28+
"""
29+
Text input widget configuration.
30+
"""
31+
type: str = Field(default='text_input', description='Widget type')
32+
label: str = Field(description='Short description')
33+
default: str = Field(default="", description='Defaults to empty string')
34+
35+
36+
@WidgetConfigRegistry.register_widget('checkbox')
37+
class CheckboxConfig(BaseModel):
38+
"""
39+
Checkbox widget configuration.
40+
"""
41+
type: str = Field(default='checkbox', description='Widget type')
42+
label: str = Field(description='Short description')
43+
default: bool = Field(default=False, description='Defaults to False')
44+
45+
46+
@WidgetConfigRegistry.register_widget('multiselect')
47+
class MultiselectConfig(BaseModel):
48+
"""
49+
Multiselect widget configuration.
50+
"""
51+
type: str = Field(default='multiselect', description='Widget type')
52+
label: str = Field(description='Short description')
53+
options: list[str] = Field(default=[], description='Comma separated list')
54+
default: list[str] = Field(default=[], description='Subset of options')
55+
56+
@model_validator(mode='after')
57+
def validate_model(self):
58+
self.default = [option for option in self.default if option in self.options]
59+
return self
60+
61+
62+
@WidgetConfigRegistry.register_widget('radio')
63+
class RadioConfig(BaseModel):
64+
"""
65+
Radio button widget configuration.
66+
"""
67+
type: str = Field(default='radio', description='Widget type')
68+
label: str = Field(description='Short description')
69+
options: list[str] = Field(default=[], description='Comma separated list')
70+
default: str | None = Field(default=None, description='Defaults to first element')
71+
horizontal: bool = Field(default=True, description='Defaults to horizontal')
72+
73+
@model_validator(mode='after')
74+
def validate_model(self):
75+
if not self.options:
76+
self.options = []
77+
if self.default is None or self.default not in self.options:
78+
self.default = self.options[0] if self.options else None
79+
return self
80+
81+
82+
@WidgetConfigRegistry.register_widget('selectbox')
83+
class SelectboxConfig(BaseModel):
84+
"""
85+
Selectbox widget configuration.
86+
"""
87+
type: str = Field(default='selectbox', description='Widget type')
88+
label: str = Field(description='Short description')
89+
options: list[str] = Field(default=[], description='Comma separated list')
90+
default: str | None = Field(default=None, description='Defaults to first element')
91+
92+
@model_validator(mode='after')
93+
def validate_model(self):
94+
if not self.options:
95+
self.options = []
96+
if self.default is None or self.default not in self.options:
97+
self.default = self.options[0] if self.options else None
98+
return self
99+
100+
101+
@WidgetConfigRegistry.register_widget('number_input')
102+
class NumberInputConfig(BaseModel):
103+
"""
104+
Number input widget configuration.
105+
"""
106+
type: str = Field(default='number_input', description='Widget type')
107+
label: str = Field(description='Short description')
108+
min_value: float = Field(default=0, description='Defaults to 0')
109+
max_value: float = Field(default=100, description='Defaults to 100')
110+
step: float = Field(default=1, description='Defaults to 1')
111+
default: float = Field(default=0, description='Defaults to min_value')
112+
113+
@model_validator(mode='after')
114+
def validate_model(self):
115+
if self.default < self.min_value:
116+
self.default = self.min_value
117+
elif self.default > self.max_value:
118+
self.default = self.max_value
119+
return self

0 commit comments

Comments
 (0)