1. Anuncie Aqui ! Entre em contato fdantas@4each.com.br

[Python] Can an instance of a child class use a parent class' methods in python? [closed]

Discussão em 'Python' iniciado por Stack, Outubro 5, 2024 às 11:32.

  1. Stack

    Stack Membro Participativo

    Problem: I have a base robot class, (gopigo3), that implements the robot's low-level functional and communication methods.

    I also have a class that inherits the base robot class, (easygopigo3), that provides a way to use the low-level methods without all the fussy details.

    I have written a basic python program, (test_servos), that moves the servos around moving the robot's pan-and-tilt side-to-side and up-and-down.

    I have two robots, (Charlie and Charlene), that both have pan-and-tilt servo mechanisms attached. Due to mounting and manufacturing tolerances, the centered-centered servo positions on one robot is different than the center-center positions of the other robot.

    To accommodate this, I read the individual robot serial numbers in my servo motion routine, (test_servos), and - based on that individual serial number - I set the appropriate center-center servo constants and make all motion relative to this centered point.

    I instantiate the easygopigo3 class to use the servo methods needed. I also instantiate the base class that is inherited by the easygopigo3 class so that I can read the serial number.

    Question: It seems to me that if easygopigo3 inherits gopigo3, then I should be able to use the methods of gopigo3 from within my instance of easygopigo3.

    The relevant part of the GoPiGo3 class (the base class) looks like this:

    # https://www.dexterindustries.com/GoPiGo/
    # https://github.com/DexterInd/GoPiGo3
    #
    # Copyright (c) 2017 Dexter Industries
    # Released under the MIT license (http://choosealicense.com/licenses/mit/).
    # For more information see https://github.com/DexterInd/GoPiGo3/blob/master/LICENSE.md
    #
    # Python drivers for the GoPiGo3

    from __future__ import print_function
    from __future__ import division
    #from builtins import input
    hardware_connected = True
    __version__ = "1.3.2"

    import subprocess # for executing system calls
    try:
    import spidev
    import fcntl # for lockf mutex support
    except:
    hardware_connected = False
    print ("Can't import spidev or fcntl")

    import math # import math for math.pi constant
    import time
    import json

    FIRMWARE_VERSION_REQUIRED = "1.0.x" # Make sure the top 2 of 3 numbers match

    import pigpio

    if hardware_connected:
    GPG_SPI = spidev.SpiDev()
    GPG_SPI.open(0, 1)
    GPG_SPI.max_speed_hz = 500000
    GPG_SPI.mode = 0b00
    GPG_SPI.bits_per_word = 8

    class GoPiGo3(object):
    WHEEL_BASE_WIDTH = 117 # distance (mm) from left wheel to right wheel. This works with the initial GPG3 prototype. Will need to be adjusted.
    WHEEL_DIAMETER = 66.5 # wheel diameter (mm)
    WHEEL_BASE_CIRCUMFERENCE = WHEEL_BASE_WIDTH * math.pi # The circumference of the circle the wheels will trace while turning (mm)
    WHEEL_CIRCUMFERENCE = WHEEL_DIAMETER * math.pi # The circumference of the wheels (mm)

    MOTOR_GEAR_RATIO = 120 # Motor gear ratio # 220 for Nicole's prototype
    ENCODER_TICKS_PER_ROTATION = 6 # Encoder ticks per motor rotation (number of magnet positions) # 16 for early prototypes
    MOTOR_TICKS_PER_DEGREE = ((MOTOR_GEAR_RATIO * ENCODER_TICKS_PER_ROTATION) / 360.0) # encoder ticks per output shaft rotation degree

    GROVE_I2C_LENGTH_LIMIT = 32

    SPI_MESSAGE_TYPE = Enumeration("""
    NONE,

    GET_MANUFACTURER,
    GET_NAME,
    GET_HARDWARE_VERSION,
    GET_FIRMWARE_VERSION,
    GET_ID,

    SET_LED,

    GET_VOLTAGE_5V,
    GET_VOLTAGE_VCC,

    SET_SERVO,

    SET_MOTOR_PWM,

    SET_MOTOR_POSITION,
    SET_MOTOR_POSITION_KP,
    SET_MOTOR_POSITION_KD,

    SET_MOTOR_DPS,

    SET_MOTOR_LIMITS,

    OFFSET_MOTOR_ENCODER,

    GET_MOTOR_ENCODER_LEFT,
    GET_MOTOR_ENCODER_RIGHT,

    GET_MOTOR_STATUS_LEFT,
    GET_MOTOR_STATUS_RIGHT,

    SET_GROVE_TYPE,
    SET_GROVE_MODE,
    SET_GROVE_STATE,
    SET_GROVE_PWM_DUTY,
    SET_GROVE_PWM_FREQUENCY,

    GET_GROVE_VALUE_1,
    GET_GROVE_VALUE_2,
    GET_GROVE_STATE_1_1,
    GET_GROVE_STATE_1_2,
    GET_GROVE_STATE_2_1,
    GET_GROVE_STATE_2_2,
    GET_GROVE_VOLTAGE_1_1,
    GET_GROVE_VOLTAGE_1_2,
    GET_GROVE_VOLTAGE_2_1,
    GET_GROVE_VOLTAGE_2_2,
    GET_GROVE_ANALOG_1_1,
    GET_GROVE_ANALOG_1_2,
    GET_GROVE_ANALOG_2_1,
    GET_GROVE_ANALOG_2_2,

    START_GROVE_I2C_1,
    START_GROVE_I2C_2,
    """)

    GROVE_TYPE = Enumeration("""
    CUSTOM = 1,
    IR_DI_REMOTE,
    IR_EV3_REMOTE,
    US,
    I2C,
    """)

    GROVE_STATE = Enumeration("""
    VALID_DATA,
    NOT_CONFIGURED,
    CONFIGURING,
    NO_DATA,
    I2C_ERROR,
    """)

    LED_EYE_LEFT = 0x02
    LED_EYE_RIGHT = 0x01
    LED_BLINKER_LEFT = 0x04
    LED_BLINKER_RIGHT = 0x08
    LED_LEFT_EYE = LED_EYE_LEFT
    LED_RIGHT_EYE = LED_EYE_RIGHT
    LED_LEFT_BLINKER = LED_BLINKER_LEFT
    LED_RIGHT_BLINKER = LED_BLINKER_RIGHT
    LED_WIFI = 0x80 # Used to indicate WiFi status. Should not be controlled by the user.

    SERVO_1 = 0x01
    SERVO_2 = 0x02

    MOTOR_LEFT = 0x01
    MOTOR_RIGHT = 0x02

    MOTOR_FLOAT = -128

    GROVE_1_1 = 0x01
    GROVE_1_2 = 0x02
    GROVE_2_1 = 0x04
    GROVE_2_2 = 0x08

    GROVE_1 = GROVE_1_1 + GROVE_1_2
    GROVE_2 = GROVE_2_1 + GROVE_2_2

    GroveType = [0, 0]
    GroveI2CInBytes = [0, 0]

    GROVE_INPUT_DIGITAL = 0
    GROVE_OUTPUT_DIGITAL = 1
    GROVE_INPUT_DIGITAL_PULLUP = 2
    GROVE_INPUT_DIGITAL_PULLDOWN = 3
    GROVE_INPUT_ANALOG = 4
    GROVE_OUTPUT_PWM = 5
    GROVE_INPUT_ANALOG_PULLUP = 6
    GROVE_INPUT_ANALOG_PULLDOWN = 7

    GROVE_LOW = 0
    GROVE_HIGH = 1

    def __init__(self, addr = 8, detect = True, config_file_path="/home/pi/Dexter/gpg3_config.json"):
    """
    Do any necessary configuration, and optionally detect the GoPiGo3

    * Optionally set the SPI address to something other than 8
    * Optionally disable the detection of the GoPiGo3 hardware. This can be used for debugging
    and testing when the GoPiGo3 would otherwise not pass the detection tests.

    The ``config_file_path`` parameter represents the path to a JSON file. The presence of this configuration file is optional and is only required in cases where
    the GoPiGo3 has a skewed trajectory due to minor differences in these two constants: the **wheel diameter** and the **wheel base width**. In most cases, this won't be the case.

    By-default, the constructor tries to read the ``config_file_path`` file and silently fails if something goes wrong: wrong permissions, non-existent file, improper key values and so on.
    To set custom values to these 2 constants, use :py:meth:`~easygopigo3.EasyGoPiGo3.set_robot_constants` method and for saving the constants to a file call
    :py:meth:`~easygopigo3.EasyGoPiGo3.save_robot_constants` method.

    """

    # Make sure the SPI lines are configured for mode ALT0 so that the hardware SPI controller can use them
    # subprocess.call('gpio mode 12 ALT0', shell=True)
    # subprocess.call('gpio mode 13 ALT0', shell=True)
    # subprocess.call('gpio mode 14 ALT0', shell=True)

    pi_gpio = pigpio.pi()
    pi_gpio.set_mode(9, pigpio.ALT0)
    pi_gpio.set_mode(10, pigpio.ALT0)
    pi_gpio.set_mode(11, pigpio.ALT0)
    pi_gpio.stop()

    self.SPI_Address = addr
    if detect == True:
    try:
    manufacturer = self.get_manufacturer()
    board = self.get_board()
    vfw = self.get_version_firmware()
    except IOError:
    raise IOError("No SPI response. GoPiGo3 with address %d not connected." % addr)
    if manufacturer != "Dexter Industries" or board != "GoPiGo3":
    raise IOError("GoPiGo3 with address %d not connected." % addr)
    if vfw.split('.')[0] != FIRMWARE_VERSION_REQUIRED.split('.')[0] or \
    vfw.split('.')[1] != FIRMWARE_VERSION_REQUIRED.split('.')[1]:
    raise FirmwareVersionError("GoPiGo3 firmware needs to be version %s but is currently version %s" \
    % (FIRMWARE_VERSION_REQUIRED, vfw))

    # load wheel diameter & wheel base width
    # also default ENCODER_TICKS_PER_ROTATION and MOTOR_GEAR_RATIO
    # should there be a problem doing that then save the current default configuration
    try:
    self.load_robot_constants(config_file_path)
    except Exception as e:
    pass

    # This is the part that reads the robot's serial number.
    def get_id(self):
    """
    Read the 128-bit GoPiGo3 hardware serial number

    Returns touple:
    serial number as 32 char HEX formatted string, error
    """
    outArray = [self.SPI_Address, self.SPI_MESSAGE_TYPE.GET_ID,\
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    reply = self.spi_transfer_array(outArray)
    if(reply[3] == 0xA5):
    return ("%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X" % \
    (reply[4], reply[5], reply[6], reply[7], reply[8], reply[9], reply[10], reply[11], \
    reply[12], reply[13], reply[14], reply[15], reply[16], reply[17], reply[18], reply[19]))
    raise IOError("No SPI response")
    return "00000000000000000000000000000000"


    The relevant part of the easygopigo3 class, (the inheriting class), looks like this:

    #!/usr/bin/env python3
    from __future__ import print_function
    from __future__ import division
    # from builtins import input

    import sys
    # import tty
    # import select
    import time
    import os
    import math
    import json
    import easysensors
    from I2C_mutex import Mutex

    __version__ = "1.3.2.1"

    try:
    from di_sensors import easy_line_follower, easy_distance_sensor, easy_light_color_sensor, easy_inertial_measurement_unit
    di_sensors_available = True
    except ImportError as err:
    di_sensors_available = False
    print("Importing di_sensors error: {}".format(err))
    except Exception as err:
    di_sensors_available = False
    print("Importing di_sensors error: {}".format(err))

    mutex = Mutex(debug=False)

    hardware_connected = True
    try:
    import gopigo3
    except ImportError:
    hardware_connected = False
    print("Cannot import gopigo3 library")
    except Exception as e:
    hardware_connected = False
    print("Unknown issue while importing gopigo3")
    print(e)

    # try:
    # from line_follower import line_sensor
    # from line_follower import scratch_line

    # # is_line_follower_accessible not really used, just in case
    # is_line_follower_accessible = True
    # except:
    # try:
    # sys.path.insert(0, '/home/pi/GoPiGo/Software/Python/line_follower')
    # import line_sensor
    # import scratch_line
    # is_line_follower_accessible = True
    # except:
    # is_line_follower_accessible = False

    ##########################


    def debug(in_str):
    if False:
    print(in_str)

    #####################################################################
    #
    # EASYGOPIGO3
    #
    #####################################################################


    class EasyGoPiGo3(gopigo3.GoPiGo3):
    """
    This class is used for controlling a `GoPiGo3`_ robot.

    With this class you can do the following things with your `GoPiGo3`_:

    * Drive your robot in any number of directions.
    * Have precise control over the direction of the robot.
    * Set the speed of the robot.
    * Turn *on* or *off* the blinker LEDs.
    * Control the `GoPiGo3`_' Dex's *eyes*, *color* and so on ...

    .. needs revisiting

    .. warning::

    Without a battery pack connected to the `GoPiGo3`_, the robot won't move.

    """

    def __init__(self, config_file_path="/home/pi/Dexter/gpg3_config.json", use_mutex=False):
    """
    This constructor sets the variables to the following values:

    :param str config_file_path = "/home/pi/Dexter/gpg3_config.json": Path to JSON config file that stores the wheel diameter and wheel base width for the GoPiGo3.
    :param boolean use_mutex = False: When using multiple threads/processes that access the same resource/device, mutex has to be enabled.
    :var int speed = 300: The speed of the motors should go between **0-1000** DPS.
    :var tuple(int,int,int) left_eye_color = (0,255,255): Set Dex's left eye color to **turqoise**.
    :var tuple(int,int,int) right_eye_color = (0,255,255): Set Dex's right eye color to **turqoise**.
    :var int DEFAULT_SPEED = 300: Starting speed value: not too fast, not too slow.
    :raises IOError: When the GoPiGo3 is not detected. It also debugs a message in the terminal.
    :raises gopigo3.FirmwareVersionError: If the GoPiGo3 firmware needs to be updated. It also debugs a message in the terminal.
    :raises Exception: For any other kind of exceptions.

    """
    try:
    if sys.version_info[0] < 3:
    super(self.__class__, self).__init__(config_file_path=config_file_path)
    else:
    super().__init__(config_file_path=config_file_path)
    except IOError as e:
    print("FATAL ERROR:\nGoPiGo3 is not detected.")
    raise e
    except gopigo3.FirmwareVersionError as e:
    print("FATAL ERROR:\nTo update the firmware on Raspbian for Robots you need to run DI Software Update and choose Update Robot")
    raise e
    except Exception as e:
    raise e

    self.sensor_1 = None
    self.sensor_2 = None
    self.DEFAULT_SPEED = 300
    self.NO_LIMIT_SPEED = 1000
    self.set_speed(self.DEFAULT_SPEED)
    self.left_eye_color = (0, 255, 255)
    self.right_eye_color = (0, 255, 255)
    self.use_mutex = use_mutex


    My "test_servos" routine wants to use the individual serial number for each robot to set the appropriate servo centering constants, since they are different. However, the method that retrieves the robot's serial number is a method of the base class (gopigo3), not the inheriting class (easygopigo3).

    My test_servos routine looks like this:

    #!/usr/bin/python3.7

    import easygopigo3 as easy
    import time
    import gopigo3 # import the GoPiGo3 drivers

    # Instantiate native GoPiGo libraries
    difficult_gpg = gopigo3.GoPiGo3() # Create an instance of the GoPiGo3 class. GPG will be the GoPiGo3 object.

    serial_number = difficult_gpg.get_id() # read and display the serial number

    # Instantiate easy GoPiGo libraries
    easy_gpg = easy.EasyGoPiGo3()
    servo_1 = easy_gpg.init_servo('SERVO1')
    servo_2 = easy_gpg.init_servo('SERVO2')
    sleep_time = 0.50 # pause time in seconds


    #Servo constants
    if serial_number == "A0F6E45F4E514B4B41202020FF152B11":
    # Charlene's servo constants
    print("Serial number is", serial_number)
    print("Robot is \"Charlene\"")
    center_1 = 86
    center_2 = 80
    right = center_1 - 45
    left = center_1 + 45
    up = center_2 - 45
    down = center_2 + 45

    elif serial_number == "A0F6E45F4E514B4B41202020FF152B12":
    # Charlie's servo constants
    center_1 = 86
    center_2 = 80
    right = center_1 - 45
    left = center_1 + 45
    up = center_2 - 45
    down = center_2 + 45

    else:
    print("I don't know who robot", serial_number, "is.")
    print("(or if it's even a robot) Aborting!")
    quit()

    # start
    print("Center Both Servos")
    servo_1.rotate_servo(center_1)
    servo_2.rotate_servo(center_2)
    time.sleep(sleep_time)

    print("Test Servo 1")
    servo_1.rotate_servo(right)
    time.sleep(sleep_time)
    servo_1.rotate_servo(left)
    time.sleep(sleep_time)
    servo_1.rotate_servo(center_1)
    time.sleep(sleep_time)

    print("Test Servo 2")
    servo_2.rotate_servo(up)
    time.sleep(sleep_time)
    servo_2.rotate_servo(down)
    time.sleep(sleep_time)

    print("Re-Center Both Servos")
    servo_1.rotate_servo(center_1)
    servo_2.rotate_servo(center_2)
    time.sleep(sleep_time)
    servo_1.disable_servo()
    servo_2.disable_servo()


    You will notice in the code above that I instantiate both classes so that I can use the base class' methods.

    If I understand the relationship between classes correctly, I should be able to use the base class' methods from within the inheriting class without creating a separate instance of the base class just to access its methods.

    I have diligently searched on-line and there is a lot of information about Python classes and inheritance. There is also a lot of information about how to use an inherited class' methods within the methods of an inheriting class.

    Unfortunately, there seems to be no information about how to use a base class' methods in an instance of the inheriting class instead of within the inheriting class' methods themselves.

    Given all this information, how do I use the base class' methods in my code's instance of the inheriting class?

    Continue reading...

Compartilhe esta Página