meseta / py-witmotion-servo

Python driver for the Witmotion 16-channel servo
MIT License
5 stars 2 forks source link

fail to work in Ubuntu 20.04 #1

Open FelixFeng-SLAM opened 2 years ago

FelixFeng-SLAM commented 2 years ago

I use witmotion 16 servo, and want control it in laptop with Ubuntu 20.04.

Firstly, I tested control in Windows 10 using a given software, it works both in position control & speed ctrl. This ensures hardware connection is ok.

In Ubuntu 20.04, this is my test.py

#!/usr/bin/python3
# -*- coding: UTF-8 -*-
import os
import sys
import time
from witmotion_servo import WitmotionServo

def main():

    wit = WitmotionServo()
    wit.open()

    # set channel 0 servo to 1000us position
    #wit.set_speed(0, 1)
    print("tesing pos ctrl--1\n")
    wit.set_position(0, 1000)
    time.sleep(2)

    # ... and then to 1500us
    print("tesing pos ctrl--2\n")
    wit.set_position(0, 1500)

    # close the connection
    wit.close()

if __name__ == '__main__': 
        main()

witmotion_servo.py is the same

""" Driver for Witmotion Servo Controller boards """

from __future__ import annotations
import time
from typing import List, Optional # remove at python 3.9
import hid

WITMOTION_VID = 0x1920
WITMOTION_PID = 0x0100

class WitmotionServo():
    """ Class for controlling Witmotion Servo Controller boards """

    @classmethod
    def list_devices(cls) -> List[str]:
        """ Lists all Witmotion servo boards plugged in
        :returns: a list of serial numbers
        """

        devices = hid.enumerate(WITMOTION_VID, WITMOTION_PID)
        return [device["serial_number"] for device in devices]

    def __init__(self, serial: Optional[str]=None, channels: int=16) -> None:
        """ Creates the hid device object
        :param serial: Optional serial number of device to connect
        :param channels: Optional number of channels the board has
        """
        self.device = hid.device()
        self.serial = serial
        self.channels = channels

    def open(self) -> WitmotionServo:
        """ Connects to the device
        device.open will raise IOError if it can't connect

        :returns: itself, facilitating method chaining
        """
        self.device.open(WITMOTION_VID, WITMOTION_PID, self.serial)
        self.device.set_nonblocking(1)
        return self

    def close(self) -> None:
        """ Closes the device """
        self.device.close()

    def heartbeat(self, timeout: float=0.1) -> List[int]:
        """ Sends the heartbeat
        :param timeout: time to wait for response before returning
        :returns: the status value array
        """
        self.device.write([1, 0x05, 0x03, 0xff, 0x00, 0x12] + [0]*58)

        time_limit = time.time_ns() + timeout*1000000000
        while time.time_ns() < time_limit:
            retval = self.device.read(64)
            if retval:
                return retval
            time.sleep(0.001)

        raise TimeoutError("Read value timed out")

    def set_position(self, channel: int, value: int, extended: bool=False) -> None:
        """ Sends servo position request
        :param channel: the channel of the servo to send
        :param value: the value (500-2500) to send
        :param extended: use extended range (0, 65535) for value
        """
        if channel < 0 or channel >= self.channels:
            raise ValueError(f"Channel out of range (0, {self.channels-1})")

        if value < 0 or value > 65535:
            raise ValueError("Value out of extended range: (0, 65535)")
        if not extended and (value < 500 or value > 2500):
            raise ValueError("Value out of range: (500, 2500)")

        datal = value & 0xff
        datah = value >> 8
        self.device.write([1, 0x05, 0x03, 0xff, 0x02, channel, datal, datah] + [0]*56)

    def set_speed(self, channel: int, value: int) -> None:
        """ Sends servo position request
        The actual speed value is 9*value degrees per second.
        e.g. speed value 15 is 135 degres per second

        :param channel: the channel of the servo to send
        :param value: the speed value (1-20)
        """
        if channel < 0 or channel >= self.channels:
            raise ValueError(f"Channel out of range (0, {self.channels-1})")

        if value < 1 or value > 20:
            raise ValueError("Value out of range: (1, 20)")

        self.device.write([1, 0x05, 0x03, 0xff, 0x01, channel, value, 0x00] + [0]*56)

    def execute_action_group(self, action_group: int) -> None:
        """ Executes an action group
        :param action_group: the action_group to execute (1-16)
        """
        if action_group < 1 or action_group > 16:
            raise ValueError("Action Group out of range (1, 16)")

        self.device.write([1, 0x05, 0x03, 0xff, 0x09, 0x00, action_group, 0x00] + [0]*56)

    def emergency_stop(self) -> None:
        """ Sends the emergency stop command """

        self.device.write([1, 0x05, 0x03, 0xff, 0x0b, 0x00, 0x01, 0x00] + [0]*56)

    def emergency_recovery(self) -> None:
        """ Sends the recover from emergency command """

        self.device.write([1, 0x05, 0x03, 0xff, 0x0b, 0x00, 0x00, 0x00] + [0]*56)

however, i test in cmd lines, it didn't work. there's no response in servo_0. In case there something wrong in channel setting, I test 0~15 channel. But it failed too.

felix@felix:~/tmp/wit16_ctrl$ python3 test.py 
tesing pos ctrl--1

tesing pos ctrl--2

felix@felix:~/tmp/wit16_ctrl$ 

this is my hidapi version:


felix@felix:~/tmp/wit16_ctrl$ pip show hidapi
Name: hidapi
Version: 0.9.0.post3
Summary: A Cython interface to the hidapi from https://github.com/libusb/hidapi
Home-page: https://github.com/trezor/cython-hidapi
Author: Gary Bishop
Author-email: gb@cs.unc.edu
License: UNKNOWN
Location: /home/felix/.local/lib/python3.8/site-packages
Requires: setuptools
Required-by: 
felix@felix:~/tmp/wit16_ctrl$