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 pi, gpio, or fake:

    • pi is the recommended model when configuring a hobby servo wired to a Raspberry Pi. Unlike other servo models, it is implemented as part of the pi board component; you can see the code here.

    • gpio is the general-purpose model, compatible with Viam-supported boards.

    • 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

The following is an example configuration file showing a board component and a servo component:

{
  "components": [
    {
      "name": "example-pi",
      "type": "board",
      "model": "pi"
    },
    {
      "name": "example-name",
      "type": "servo",
      "model": "pi",
      "attributes": {
        "pin": "16"
      },
      "depends_on": [
        "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 rate of pulses sent to the servo. The servo driver will attempt to change the GPIO pin’s frequency (in Hz). The recommended PWM frequency for servos is typically in the range of 40-200 Hz, with most servos using 50 Hz (see your servo’s data sheet). Maximum supported frequency by this driver is 450Hz
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')

# 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())