Servo Component

Explanation of servo wiring and configuration in Viam.

Hobby servos are a type of actuator comprising a small motor with built-in closed-loop control.

The Viam servo component is designed to support hobby servos, not servomotors. Configure an industrial servomotor as a motor with an encoder.

Hardware Requirements

A typical servo control setup comprises the following:

  • A Raspberry Pi (or other board)
  • A servo
  • An appropriate power supply

Wiring Example

Here’s an example of how a servo might be wired to a Raspberry Pi:

A diagram showing the signal wire of a servo connected to pin 16 on a Raspberry Pi. The servo’s power wires are connected to a 4.8V power supply.

Viam Configuration

The following fields are required when configuring a servo:

  • Name: A name of the user’s choosing by which to identify the component

  • Type: servo for all servos

  • Model: Either gpio, pi, or fake:

    • gpio is the recommended general-purpose model, compatible with all Viam-supported boards including Raspberry Pi.

    • pi is only compatible with Raspberry Pi. It has a timeout parameter to help prevent burning out cheap servos, but this is not necessary in most cases.

    • fake is for testing code without any actual hardware.

  • Attributes: Other details the component requires to function. All models require the following:

    • pin (string): The board pin (with PWM capabilities) to which the servo’s control wire is attached. Use pin number, not GPIO number.

    • board (string): The name of the board to which the servo is wired.

    • Some models have additional attributes which are described below.

Example Config

An example configuration file containing the necessary attributes is as follows:

{
  "components": [
    {
      "name": "example-pi",
      "type": "board",
      "model": "pi"
      
    },
    {
      "name": "example-name",
      "type": "servo",
      "model": "gpio",
      "attributes": {
        "pin": "16",
        "board": "example-pi"
      }
    }
  ]
}

An example servo config file with explanatory annotations.

Optional Attributes: GPIO Model

Attribute NameTypeDescription
min_angle_degfloat64Specifies the minimum angle in degrees to which the servo can move. Does not affect PWM calculation.
max_angle_degfloat64Specifies the maximum angle in degrees to which the servo can move. Does not affect PWM calculation.
starting_position_degfloat64Starting position of the servo in degrees.
frequency_hzuintThe servo driver will attempt to change the GPIO pin’s frequency. Default for a Pi is 19.2MHz.
pwm_resolutionuintResolution of the PWM driver (e.g. number of ticks for a full period). Must be in range (0, 450). If not specified, the driver will attempt to estimate the resolution.
min_width_usuintOverride the safe minimum pulse width in microseconds. This affects PWM calculation.
max_width_usuintOverride the safe maximum pulse width in microseconds. This affects PWM calculation.

API

The servo component supports the following methods:

Method NameGolangPythonDescription
MoveMovemoveMove the servo to the provided angle.
Get PositionGetPositionget_positionReturns an int representing the current angle of the servo in degrees.
StopStopstopStops the servo.

Controlling a servo via SDK

from viam.components.servo import ServoClient

async def main():
    robot = await connect()

    print('Resources:')
    print(robot.resource_names)

    # Get the servo client from the robot
    myServo = ServoClient.from_robot(robot=robot, name='my_servo')

    await robot.close()

if __name__ == '__main__':
    asyncio.run(main())

import (
 "go.viam.com/rdk/components/servo"
)

func main() {
  // robot, err := client.New(...)

  logger.Info("Resources:")
  logger.Info(robot.ResourceNames())

  // Get the servo client from the robot.
  myServo, err := servo.FromRobot(robot, "my_servo")
  if err != nil {
    logger.Fatalf("cannot get servo: %v", err)
  }
}

Move

Move requests the servo of the underlying robot to move.

Parameters:

  • angle (int): The desired angle of the servo in degrees.

Returns:

  • None

Python SDK Move Documentation

myServo = ServoClient.from_robot(robot=robot, name='my_servo')

# Move the servo to the provided angle, which is 10 degrees in this case
await myServo.move(10)

# Move the servo to 90 degrees
await myServo.move(90)

Parameters:

  • Context: A Context carries a deadline, a cancellation signal, and other values across API boundaries.
  • angleDeg (uint8): The desired angle of the servo in degrees.
  • extra (map[string]interface{}): Extra options to pass to the underlying RPC call.

Returns:

  • error: An error if one occurred.

Go SDK Move Documentation

myServo, err := servo.FromRobot(robot, "my_servo")
if err != nil {
  logger.Fatalf("cannot get servo: %v", err)
}

// Move the servo to the provided angle, which is 10 degrees in this case
myServo.Move(context.Background(), 10)

// Move the servo to the 90 degrees
myServo.Move(context.Background(), 90)

Get Position

Get Position returns an int representing the current angle of the servo in degrees.

Parameters:

  • None

Returns:

  • The current angle of the servo in degrees. (int)

Python SDK Get Position Documentation

myServo = ServoClient.from_robot(robot=robot, name='my_servo')
if err != nil {
  logger.Fatalf("cannot get servo: %v", err)
}
# Move the servo to the provided angle, which is 10 degrees in this case
await myServo.move(10)

# Get the current position of the servo, which returns 10
await myServo.get_position()

# Move the servo to 20 degrees
await myServo.move(20)

# Get the current position of the servo, which returns 20
await myServo.get_position()

Parameters:

  • Context: A Context carries a deadline, a cancellation signal, and other values across API boundaries.
  • extra (map[string]interface{}): Extra options to pass to the underlying RPC call.

Returns:

  • angleDeg (uint8): The current angle of the servo in degrees.
  • error: An error if one occurred.

Go SDK Get Position Documentation

myServo, err := servo.FromRobot(robot, "my_servo")
if err != nil {
  logger.Fatalf("cannot get servo: %v", err)
}

// Move the servo to the provided angle, which is 10 degrees in this case
myServo.Move(context.Background(), 10)

// Move the servo to 20 degrees
myServo.Move(context.Background(), 20)

Stop

Stops the servo. It is assumed that the servo stops immediately.

Parameters:

  • None

Returns:

  • None.

Python SDK Stop Documentation

myServo = ServoClient.from_robot(robot=robot, name='my_servo')

# Move the servo to the provided angle, which is 10 degrees in this case
await myServo.move(10)

# Stop the servo
await myServo.stop()

Parameters:

  • Context: A Context carries a deadline, a cancellation signal, and other values across API boundaries.
  • extra (map[string]interface{}): Extra options to pass to the underlying RPC call.

Returns:

  • error: An error if one occurred.

Go SDK Stop Documentation

myServo, err := servo.FromRobot(robot, "my_servo")
if err != nil {
  logger.Fatalf("cannot get servo: %v", err)
}

// Move the servo to the provided angle, which is 10 degrees in this case
myServo.Move(context.Background(), 10)

// Stop the servo
myServo.Stop(context.Background())