# Vassar Feetech Servo SDK
[](https://badge.fury.io/py/vassar-feetech-servo-sdk)
[](https://github.com/vassar-robotics/feetech-servo-sdk)
A comprehensive Python SDK for controlling Feetech servos (STS/HLS series).
## Features
- 🔌 **Auto-detection** of serial ports
- 🎯 **Support for STS and HLS servos** (HLS includes torque control)
- 📖 **Read positions** from single or multiple servos
- âš¡ **Read voltage** from servos for monitoring power status
- 🎯 **Set middle position** - Calibrate servos to position 2048
- 💪 **Write torque targets** - HLS servos only with automatic mode switching
- 🔧 **Set operating modes** - Configure servo behavior (position/speed/torque/PWM)
## Installation
### From PyPI
```bash
pip install vassar-feetech-servo-sdk
```
### From Source
```bash
git clone https://github.com/vassar-robotics/feetech-servo-sdk.git
cd feetech-servo-sdk
pip install -e .
```
## Dependencies
- Python >= 3.7
- pyserial >= 3.5
Note: The `scservo_sdk` is bundled with this package, so no separate installation is needed.
## Quick Start
```python
from vassar_feetech_servo_sdk import ServoController
# Initialize controller with your servo configuration
servo_ids = [1, 2, 3, 4, 5, 6]
controller = ServoController(servo_ids=servo_ids, servo_type="sts") # or "hls"
controller.connect()
# Read all configured servos
positions = controller.read_all_positions()
for motor_id, pos in positions.items():
print(f"Motor {motor_id}: {pos} ({pos/4095*100:.1f}%)")
# Set servos to middle position
success = controller.set_middle_position()
if success:
print("All servos calibrated to middle position!")
controller.disconnect()
# Using context manager
with ServoController([1, 2, 3], "sts") as controller:
positions = controller.read_all_positions()
print(positions)
```
### Changing Servo IDs
```python
from vassar_feetech_servo_sdk import ServoController
# Connect to a servo with current ID 1
controller = ServoController(servo_ids=[1], servo_type="sts")
controller.connect()
# Change its ID from 1 to 10
success = controller.set_motor_id(
current_id=1,
new_id=10,
confirm=True # Will ask for user confirmation
)
if success:
print("ID changed! Power cycle the servo to apply.")
controller.disconnect()
# After power cycling, connect with new ID
controller = ServoController(servo_ids=[10], servo_type="sts")
controller.connect()
```
### Position Control
```python
from vassar_feetech_servo_sdk import ServoController
# Connect to servos (STS or HLS)
controller = ServoController(servo_ids=[1, 2, 3], servo_type="hls")
controller.connect()
# Write position values (automatically switches to position mode)
positions = {
1: 1024, # ~90° (position 0-4095)
2: 2048, # ~180° (middle position)
3: 3072 # ~270°
}
results = controller.write_position(positions) # Uses default speed=100
print(f"Position write results: {results}")
# Position control with speed and acceleration
results = controller.write_position(
positions,
speed=60, # 60 * 0.732 = ~44 RPM
acceleration=50 # 50 * 8.7 = 435°/s²
)
# For HLS servos only: Position control with torque limit
if controller.servo_type == "hls":
positions_with_limit = {1: 2048, 2: 2048}
torque_limits = {1: 0.5, 2: 0.8} # 50% and 80% torque limit
results = controller.write_position(positions_with_limit, torque_limits, speed=40)
print(f"Position write with torque limit: {results}")
controller.disconnect()
```
### Voltage Reading
```python
from vassar_feetech_servo_sdk import ServoController
# Connect to servos
with ServoController([1, 2, 3, 4, 5, 6], "sts") as controller:
# Read voltage from single servo
voltage = controller.read_voltage(1)
print(f"Servo 1 voltage: {voltage:.1f}V")
# Read voltages from all servos
voltages = controller.read_voltages()
for motor_id, v in sorted(voltages.items()):
print(f"Servo {motor_id}: {v:.1f}V")
# Detect leader/follower arms based on voltage
if voltages[1] < 9.0:
print("Servo 1 is on the leader arm (< 9V)")
else:
print("Servo 1 is on the follower arm (> 9V)")
```
### Torque Control (HLS Only)
```python
from vassar_feetech_servo_sdk import ServoController
# Connect to HLS servos
controller = ServoController(servo_ids=[1, 2, 3], servo_type="hls")
controller.connect()
# Write torque values (automatically switches to torque mode)
torque_values = {
1: 0.04, # 4% forward torque
2: -0.06, # 6% reverse torque
3: 0 # No torque
}
results = controller.write_torque(torque_values)
print(f"Torque write results: {results}")
# You can also manually set operating modes
controller.set_operating_mode(1, 0) # Position mode
controller.set_operating_mode(2, 1) # Speed mode
controller.set_operating_mode(3, 2) # Torque mode
controller.disconnect()
```
### Advanced Usage
```python
# Initialize with specific configuration
controller = ServoController(
servo_ids=[1, 2, 3, 4, 5, 6],
servo_type="hls", # 'sts' or 'hls'
port="/dev/ttyUSB0",
baudrate=1000000
)
# Error handling
from vassar_feetech_servo_sdk import ServoReaderError, PortNotFoundError
try:
controller = ServoController([1, 2, 3], "sts")
controller.connect()
positions = controller.read_all_positions()
except PortNotFoundError:
print("No servo port found!")
except ServoReaderError as e:
print(f"Error: {e}")
```
## API Reference
### ServoController Class
#### Constructor
```python
ServoController(servo_ids, servo_type="sts", port=None, baudrate=1000000)
```
- `servo_ids`: List of servo IDs to control (e.g., [1, 2, 3, 4, 5, 6])
- `servo_type`: Type of servo - 'sts' or 'hls' (default: 'sts')
- `port`: Serial port path (auto-detect if None)
- `baudrate`: Communication speed (default: 1000000)
#### Methods
- `connect()`: Establish connection to servos
- `disconnect()`: Close connection
- `read_position(motor_id)`: Read single motor position
- `read_positions(motor_ids=None)`: Read multiple motor positions
- `read_all_positions()`: Read all configured servo positions
- `read_voltage(motor_id)`: Read voltage from single servo (returns float in volts)
- `read_voltages(motor_ids=None)`: Read voltages from multiple servos (returns dict of voltages)
- `set_middle_position(motor_ids=None)`: Calibrate servos to middle position (2048)
- `set_motor_id(current_id, new_id, confirm=True)`: Change a servo's ID (requires power cycle)
- `set_operating_mode(motor_id, mode)`: Set servo operating mode (0-3)
- `write_position(position_dict, torque_limit_dict=None, speed=100, acceleration=0)`: Write position values to servos (auto-switches to position mode)
- `write_torque(torque_dict)`: Write torque values to HLS servos (auto-switches to torque mode)
- `disable_all_servos()`: Disable torque on all servos (called automatically on cleanup)
**Note**: The controller automatically disables all servos when the object is destroyed or when using context manager (with statement).
### Utility Functions
- `find_servo_port()`: Auto-detect servo serial port
## Servo Types
- **STS**: Standard Feetech servos (default) - position and speed control
- Middle position calibration uses torque=128 method
- **HLS**: High-end servos with additional torque control capabilities
- Middle position calibration uses offset calibration (`reOfsCal`) method
## Troubleshooting
### Port Not Found
If auto-detection fails, specify the port manually:
```python
# Linux
controller = ServoController(servo_ids=[1,2,3], port="/dev/ttyUSB0")
# macOS
controller = ServoController(servo_ids=[1,2,3], port="/dev/tty.usbserial-XXXXX")
# Windows
controller = ServoController(servo_ids=[1,2,3], port="COM3")
```
### Permission Denied (Linux)
Option 1: Add your user to the dialout group (recommended):
```bash
sudo usermod -a -G dialout $USER
# Log out and back in for changes to take effect
```
Option 2: Grant permissions to the serial port (temporary):
```bash
sudo chmod 666 /dev/ttyUSB0
# Replace /dev/ttyUSB0 with your actual serial port
```
### Connection Failed
1. Check servo power supply
2. Verify baudrate matches servo configuration (default: 1000000)
3. Ensure proper wiring (TX/RX not swapped)
## Examples
The package includes several example scripts in the `examples/` directory:
- `basic_usage.py` - Simple example showing how to connect and read servo positions
- `continuous_reading.py` - Real-time monitoring with custom callbacks
- `servo_types.py` - Demonstrates differences between STS and HLS servos
- `set_middle_position.py` - Shows how to calibrate servos to middle position
- `position_control.py` - Comprehensive position control examples
- `torque_control.py` - HLS torque control examples
- `change_servo_id.py` - How to change servo IDs
- `read_voltage.py` - Reading voltage from servos
- `teleoperation.py` - Leader-follower arm control with voltage-based auto-detection
## Testing
Run the test suite:
```bash
./run_tests.sh # Installs dev dependencies and runs tests with coverage
# or
pip install -r requirements-dev.txt
python -m pytest tests/ -v
```
## Acknowledgments
Built on top of the excellent `scservo_sdk` library for Feetech servo communication.
Raw data
{
"_id": null,
"home_page": null,
"name": "vassar-feetech-servo-sdk",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": "Vassar Robotics <hello@vassarrobotics.com>",
"keywords": "robotics, servo, feetech, serial, hardware, sts, hls, control",
"author": null,
"author_email": "Vassar Robotics <hello@vassarrobotics.com>",
"download_url": "https://files.pythonhosted.org/packages/61/bc/f6df1bf5abf95ab471bfc892a2669d84a61ba47801eb222c6afab38d4c0a/vassar_feetech_servo_sdk-1.4.0.tar.gz",
"platform": null,
"description": "# Vassar Feetech Servo SDK\n\n[](https://badge.fury.io/py/vassar-feetech-servo-sdk)\n[](https://github.com/vassar-robotics/feetech-servo-sdk)\n\nA comprehensive Python SDK for controlling Feetech servos (STS/HLS series).\n\n## Features\n\n- \ud83d\udd0c **Auto-detection** of serial ports\n- \ud83c\udfaf **Support for STS and HLS servos** (HLS includes torque control)\n- \ud83d\udcd6 **Read positions** from single or multiple servos \n- \u26a1 **Read voltage** from servos for monitoring power status\n- \ud83c\udfaf **Set middle position** - Calibrate servos to position 2048\n- \ud83d\udcaa **Write torque targets** - HLS servos only with automatic mode switching\n- \ud83d\udd27 **Set operating modes** - Configure servo behavior (position/speed/torque/PWM)\n\n## Installation\n\n### From PyPI\n\n```bash\npip install vassar-feetech-servo-sdk\n```\n\n### From Source\n\n```bash\ngit clone https://github.com/vassar-robotics/feetech-servo-sdk.git\ncd feetech-servo-sdk\npip install -e .\n```\n\n## Dependencies\n\n- Python >= 3.7\n- pyserial >= 3.5\n\nNote: The `scservo_sdk` is bundled with this package, so no separate installation is needed.\n\n## Quick Start\n\n```python\nfrom vassar_feetech_servo_sdk import ServoController\n\n# Initialize controller with your servo configuration\nservo_ids = [1, 2, 3, 4, 5, 6]\ncontroller = ServoController(servo_ids=servo_ids, servo_type=\"sts\") # or \"hls\"\ncontroller.connect()\n\n# Read all configured servos\npositions = controller.read_all_positions()\nfor motor_id, pos in positions.items():\n print(f\"Motor {motor_id}: {pos} ({pos/4095*100:.1f}%)\")\n\n# Set servos to middle position\nsuccess = controller.set_middle_position()\nif success:\n print(\"All servos calibrated to middle position!\")\n\ncontroller.disconnect()\n\n# Using context manager\nwith ServoController([1, 2, 3], \"sts\") as controller:\n positions = controller.read_all_positions()\n print(positions)\n```\n\n### Changing Servo IDs\n\n```python\nfrom vassar_feetech_servo_sdk import ServoController\n\n# Connect to a servo with current ID 1\ncontroller = ServoController(servo_ids=[1], servo_type=\"sts\")\ncontroller.connect()\n\n# Change its ID from 1 to 10\nsuccess = controller.set_motor_id(\n current_id=1,\n new_id=10,\n confirm=True # Will ask for user confirmation\n)\n\nif success:\n print(\"ID changed! Power cycle the servo to apply.\")\n \ncontroller.disconnect()\n\n# After power cycling, connect with new ID\ncontroller = ServoController(servo_ids=[10], servo_type=\"sts\")\ncontroller.connect()\n```\n\n### Position Control\n\n```python\nfrom vassar_feetech_servo_sdk import ServoController\n\n# Connect to servos (STS or HLS)\ncontroller = ServoController(servo_ids=[1, 2, 3], servo_type=\"hls\")\ncontroller.connect()\n\n# Write position values (automatically switches to position mode)\npositions = {\n 1: 1024, # ~90\u00b0 (position 0-4095)\n 2: 2048, # ~180\u00b0 (middle position)\n 3: 3072 # ~270\u00b0\n}\n\nresults = controller.write_position(positions) # Uses default speed=100\nprint(f\"Position write results: {results}\")\n\n# Position control with speed and acceleration\nresults = controller.write_position(\n positions, \n speed=60, # 60 * 0.732 = ~44 RPM\n acceleration=50 # 50 * 8.7 = 435\u00b0/s\u00b2\n)\n\n# For HLS servos only: Position control with torque limit\nif controller.servo_type == \"hls\":\n positions_with_limit = {1: 2048, 2: 2048}\n torque_limits = {1: 0.5, 2: 0.8} # 50% and 80% torque limit\n \n results = controller.write_position(positions_with_limit, torque_limits, speed=40)\n print(f\"Position write with torque limit: {results}\")\n\ncontroller.disconnect()\n```\n\n### Voltage Reading\n\n```python\nfrom vassar_feetech_servo_sdk import ServoController\n\n# Connect to servos\nwith ServoController([1, 2, 3, 4, 5, 6], \"sts\") as controller:\n # Read voltage from single servo\n voltage = controller.read_voltage(1)\n print(f\"Servo 1 voltage: {voltage:.1f}V\")\n \n # Read voltages from all servos\n voltages = controller.read_voltages()\n for motor_id, v in sorted(voltages.items()):\n print(f\"Servo {motor_id}: {v:.1f}V\")\n \n # Detect leader/follower arms based on voltage\n if voltages[1] < 9.0:\n print(\"Servo 1 is on the leader arm (< 9V)\")\n else:\n print(\"Servo 1 is on the follower arm (> 9V)\")\n```\n\n### Torque Control (HLS Only)\n\n```python\nfrom vassar_feetech_servo_sdk import ServoController\n\n# Connect to HLS servos\ncontroller = ServoController(servo_ids=[1, 2, 3], servo_type=\"hls\")\ncontroller.connect()\n\n# Write torque values (automatically switches to torque mode)\ntorque_values = {\n 1: 0.04, # 4% forward torque\n 2: -0.06, # 6% reverse torque \n 3: 0 # No torque\n}\n\nresults = controller.write_torque(torque_values)\nprint(f\"Torque write results: {results}\")\n\n# You can also manually set operating modes\ncontroller.set_operating_mode(1, 0) # Position mode\ncontroller.set_operating_mode(2, 1) # Speed mode\ncontroller.set_operating_mode(3, 2) # Torque mode\n\ncontroller.disconnect()\n```\n\n### Advanced Usage\n\n```python\n# Initialize with specific configuration\ncontroller = ServoController(\n servo_ids=[1, 2, 3, 4, 5, 6],\n servo_type=\"hls\", # 'sts' or 'hls'\n port=\"/dev/ttyUSB0\",\n baudrate=1000000\n)\n\n# Error handling\nfrom vassar_feetech_servo_sdk import ServoReaderError, PortNotFoundError\n\ntry:\n controller = ServoController([1, 2, 3], \"sts\")\n controller.connect()\n positions = controller.read_all_positions()\nexcept PortNotFoundError:\n print(\"No servo port found!\")\nexcept ServoReaderError as e:\n print(f\"Error: {e}\")\n```\n\n## API Reference\n\n### ServoController Class\n\n#### Constructor\n\n```python\nServoController(servo_ids, servo_type=\"sts\", port=None, baudrate=1000000)\n```\n\n- `servo_ids`: List of servo IDs to control (e.g., [1, 2, 3, 4, 5, 6])\n- `servo_type`: Type of servo - 'sts' or 'hls' (default: 'sts')\n- `port`: Serial port path (auto-detect if None)\n- `baudrate`: Communication speed (default: 1000000)\n\n#### Methods\n\n- `connect()`: Establish connection to servos\n- `disconnect()`: Close connection\n- `read_position(motor_id)`: Read single motor position\n- `read_positions(motor_ids=None)`: Read multiple motor positions\n- `read_all_positions()`: Read all configured servo positions\n- `read_voltage(motor_id)`: Read voltage from single servo (returns float in volts)\n- `read_voltages(motor_ids=None)`: Read voltages from multiple servos (returns dict of voltages)\n- `set_middle_position(motor_ids=None)`: Calibrate servos to middle position (2048)\n- `set_motor_id(current_id, new_id, confirm=True)`: Change a servo's ID (requires power cycle)\n- `set_operating_mode(motor_id, mode)`: Set servo operating mode (0-3)\n- `write_position(position_dict, torque_limit_dict=None, speed=100, acceleration=0)`: Write position values to servos (auto-switches to position mode)\n- `write_torque(torque_dict)`: Write torque values to HLS servos (auto-switches to torque mode)\n- `disable_all_servos()`: Disable torque on all servos (called automatically on cleanup)\n\n**Note**: The controller automatically disables all servos when the object is destroyed or when using context manager (with statement).\n\n### Utility Functions\n\n- `find_servo_port()`: Auto-detect servo serial port\n\n## Servo Types\n\n- **STS**: Standard Feetech servos (default) - position and speed control\n - Middle position calibration uses torque=128 method\n- **HLS**: High-end servos with additional torque control capabilities\n - Middle position calibration uses offset calibration (`reOfsCal`) method\n\n## Troubleshooting\n\n### Port Not Found\n\nIf auto-detection fails, specify the port manually:\n\n```python\n# Linux\ncontroller = ServoController(servo_ids=[1,2,3], port=\"/dev/ttyUSB0\")\n\n# macOS\ncontroller = ServoController(servo_ids=[1,2,3], port=\"/dev/tty.usbserial-XXXXX\")\n\n# Windows\ncontroller = ServoController(servo_ids=[1,2,3], port=\"COM3\")\n```\n\n### Permission Denied (Linux)\n\nOption 1: Add your user to the dialout group (recommended):\n\n```bash\nsudo usermod -a -G dialout $USER\n# Log out and back in for changes to take effect\n```\n\nOption 2: Grant permissions to the serial port (temporary):\n\n```bash\nsudo chmod 666 /dev/ttyUSB0\n# Replace /dev/ttyUSB0 with your actual serial port\n```\n\n### Connection Failed\n\n1. Check servo power supply\n2. Verify baudrate matches servo configuration (default: 1000000)\n3. Ensure proper wiring (TX/RX not swapped)\n\n## Examples\n\nThe package includes several example scripts in the `examples/` directory:\n\n- `basic_usage.py` - Simple example showing how to connect and read servo positions\n- `continuous_reading.py` - Real-time monitoring with custom callbacks\n- `servo_types.py` - Demonstrates differences between STS and HLS servos\n- `set_middle_position.py` - Shows how to calibrate servos to middle position\n- `position_control.py` - Comprehensive position control examples\n- `torque_control.py` - HLS torque control examples\n- `change_servo_id.py` - How to change servo IDs\n- `read_voltage.py` - Reading voltage from servos\n- `teleoperation.py` - Leader-follower arm control with voltage-based auto-detection\n\n## Testing\n\nRun the test suite:\n\n```bash\n./run_tests.sh # Installs dev dependencies and runs tests with coverage\n# or\npip install -r requirements-dev.txt\npython -m pytest tests/ -v\n```\n\n## Acknowledgments\n\nBuilt on top of the excellent `scservo_sdk` library for Feetech servo communication.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Vassar Robotics' Python SDK for Feetech servo control",
"version": "1.4.0",
"project_urls": {
"Homepage": "https://github.com/vassar-robotics/feetech-servo-sdk",
"Issues": "https://github.com/vassar-robotics/feetech-servo-sdk/issues",
"Repository": "https://github.com/vassar-robotics/feetech-servo-sdk"
},
"split_keywords": [
"robotics",
" servo",
" feetech",
" serial",
" hardware",
" sts",
" hls",
" control"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "f1b8e6c0cb0b73dc93534c4113384f3a9b360e56daf3afdcefe4342263e90070",
"md5": "bec2ff61ee9ae52f0a3c4126910e6a58",
"sha256": "111d255847a4ed2029df77aced913efd95d9649b49c3ac6901d62d9d07d93871"
},
"downloads": -1,
"filename": "vassar_feetech_servo_sdk-1.4.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "bec2ff61ee9ae52f0a3c4126910e6a58",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 32708,
"upload_time": "2025-08-20T18:25:30",
"upload_time_iso_8601": "2025-08-20T18:25:30.534288Z",
"url": "https://files.pythonhosted.org/packages/f1/b8/e6c0cb0b73dc93534c4113384f3a9b360e56daf3afdcefe4342263e90070/vassar_feetech_servo_sdk-1.4.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "61bcf6df1bf5abf95ab471bfc892a2669d84a61ba47801eb222c6afab38d4c0a",
"md5": "fa40b30db1fe0535033a53bf4e86b5e7",
"sha256": "9f4778ba64c433846b55ce43b85ff458fb40233f62b9cbe6507ef9ebb5deedbd"
},
"downloads": -1,
"filename": "vassar_feetech_servo_sdk-1.4.0.tar.gz",
"has_sig": false,
"md5_digest": "fa40b30db1fe0535033a53bf4e86b5e7",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 30529,
"upload_time": "2025-08-20T18:25:32",
"upload_time_iso_8601": "2025-08-20T18:25:32.634280Z",
"url": "https://files.pythonhosted.org/packages/61/bc/f6df1bf5abf95ab471bfc892a2669d84a61ba47801eb222c6afab38d4c0a/vassar_feetech_servo_sdk-1.4.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-20 18:25:32",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "vassar-robotics",
"github_project": "feetech-servo-sdk",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "vassar-feetech-servo-sdk"
}