Ejemplo n.º 1
0
class Encoders:
    default_unit = 'rad/s'

    def __init__(self, countsPerRevolution, units=default_unit):
        self.countsPerRevolution = float(countsPerRevolution)
        print 'self.countsPerRevolution : {0}'.format(self.countsPerRevolution)
        self.max_counts = 2**30  #set max counts below max integer regular int size
        self.unitConversionMultiplier = None
        self.__setVelocityUnits(units)

        #set encoder direction defaults
        #may be modified to modify interpreted direction of encoder to match intended ESC input
        self.encoder_direction = {0: 1, 1: 1, 2: 1}

        #Create an encoder object
        try:
            self.encoder = Encoder()
        except RuntimeError as e:
            raise RuntimeError("Runtime Exception: {0:s}".format(e.details))

        #Set event handler behavior
        try:
            self.encoder.setOnAttachHandler(self.__encoderAttached)
            self.encoder.setOnDetachHandler(self.__encoderDetached)
            self.encoder.setOnErrorhandler(self.__encoderError)
        except PhidgetException as e:
            raise RuntimeError("Phidget Error {0}: {1}".format(
                e.code, e.details))

        try:
            self.encoder.openPhidget()
            self.encoder.waitForAttach(10000)
        except PhidgetException as e:
            raise RuntimeError(
                "Phidget Error {0}: {1}.\nFailed 'openPhidget()'.".format(
                    e.code, e.details))

        time.sleep(
            2
        )  #pause here to avoid starting motors before encoders fully initialized

        #set all encoder positions to 0
        for i in xrange(3):
            self.__resetCounts(i)

        self.time_init = time.time()
        self.prevCountArray = [self.time_init, 0, 0, 0]

    #Event Handler Callback Functions
    def __encoderAttached(self, e):
        attached = e.device
        print "Encoder {0} Attached!".format(attached.getSerialNum())
        #self.__displayDeviceInfo()

    def __encoderDetached(self, e):
        detached = e.device
        raise RuntimeError("Encoder {0} Detached!".format(
            detached.getSerialNum()))

    def __encoderError(self, e):
        try:
            source = e.device
            raise RuntimeError("Encoder {0}: Phidget Error {1}: {2}".format(
                source.getSerialNum(), e.eCode, e.description))
        except PhidgetException as e:
            raise RuntimeError("Phidget Exception {0}: {1}".format(
                e.code, e.details))

    #External Methods
    def resetCounter(self, index):
        self.encoder.setPostion(index)

    def getVelocities(self):
        #return instantaneous velocities for each encoder
        print '\n'
        print 'self.unitConversionMultiplier : {0}'.format(
            self.unitConversionMultiplier)
        count_array = self.returnCountArray()
        print 'count_array : {0}'.format(count_array)
        print 'prevCountArray : {0}'.format(self.prevCountArray)
        diff_array = [
            float(j - self.prevCountArray[i])
            for i, j in enumerate(count_array)
        ]
        print 'diff_array : {0}'.format(diff_array)
        self.prevCountArray = [i for i in count_array]
        velocities = [count_array[0]
                      ] + self.__returnVelocitiesFromCounts(diff_array)
        print 'velocities : {0}'.format(velocities)

        #check if any encoders need to be reset to avoid exceeding max integer value
        #if so, reset them
        encoders_to_reset = [
            i for i, x in enumerate(count_array[1:])
            if abs(x) > self.max_counts
        ]
        for i in encoders_to_reset:
            print 'Reset counts on index {0} from {1} to 0 to avoid int overflow.'.format(
                i, )
            self.__resetCounts(i)

        return velocities

    def reverseDirection(self, index):
        #set interpreted spin direction of an encoder channel
        self.encoder_direction[index] *= -1
        return None

    def returnCountArray(self):
        #return counts array:
        #[time of measurement, encoder 0 count, encoder 1 count, encoder 2 count]
        counts_array = []
        counts_array = [time.time()] + [
            self.encoder.getPosition(i) * self.encoder_direction[i]
            for i in xrange(3)
        ]
        return counts_array

    #Internal Methods
    def __returnVelocitiesFromCounts(self, diffCountsArray):
        #convert counts to velocity in selected units
        return [
            i / diffCountsArray[0] / self.countsPerRevolution *
            self.unitConversionMultiplier for i in diffCountsArray[1:]
        ]

    def __returnEncoderTime(self, time_init):
        return time.time() - self.time_init

    def __displayDeviceInfo():
        #Information Display Function
        print(
            "|------------|----------------------------------|--------------|------------|"
        )
        print(
            "|- Attached -|-              Type              -|- Serial No. -|-  Version -|"
        )
        print(
            "|------------|----------------------------------|--------------|------------|"
        )
        print("|- {0:8} -|- {1:30s} -|- {2:10d} -|- {3:8d} -|".format(
            encoder.isAttached(), encoder.getDeviceName(),
            encoder.getSerialNum(), encoder.getDeviceVersion()))
        print(
            "|------------|----------------------------------|--------------|------------|"
        )

    def __resetCounts(self, index):
        #reset encoder channel position to zero
        self.encoder.setPosition(index, 0)

    def __setVelocityUnits(self, units):
        #toggle output between rad/s, Hz, rpm
        #multipliers for converting from Hz
        #self.unitConversion is used in __returnVelocitiesFromCounts to get to desired units
        unitsConversion = {'rad/s': (2.0 * pi), 'Hz': 1, 'rpm': 60.0}
        if units in unitsConversion:
            self.unitConversionMultiplier = unitsConversion[units]
            print 'Units set to {0}.'.format(units)
        else:
            self.unitConversionMultiplier = unitsConversion[default_unit]
            print('Requested units ({0}) not available. Using {1} instead.'.
                  format(units, default_unit))
Ejemplo n.º 2
0
class PhidgetsController(kp.Module):
    def configure(self):
        from Phidgets.Devices.Stepper import Stepper
        from Phidgets.Devices.Encoder import Encoder

        self.current_limit = self.get("current_limit") or 2.5
        self.motor_id = self.get("motor_id") or 0
        self.stepper = Stepper()
        self.encoder = Encoder()
        self.setup(self.motor_id)

    def setup(self, motor_id):
        self.stepper.openPhidget()
        self.stepper.waitForAttach(10000)

        self.encoder.openPhidget()
        self.encoder.waitForAttach(10000)

        self.stepper.setVelocityLimit(motor_id, 1000)
        self.stepper.setAcceleration(motor_id, 5000)
        self.stepper.setCurrentLimit(motor_id, 2.5)

        self.e = 13250.0
        self.s = 70500.0

        self._stepper_dest = 0
        self._encoder_dest = 0

        self.reset_positions()

    def drive_to_angle(self, ang, motor_id=0, relative=False):
        stepper_dest = self._stepper_dest = self.raw_stepper_position(ang)
        self._encoder_dest = self.raw_encoder_position(ang)

        if relative:
            self.reset_positions()

        self.wake_up()
        time.sleep(0.1)

        self.stepper_target_pos = stepper_dest
        self.wait_for_stepper()
        self.log_offset()

        while abs(self.offset) > 1:
            self.log_offset()
            stepper_offset = round(self.offset / self.e * self.s)
            log.debug("Correcting stepper by {0}".format(stepper_offset))
            log.debug("Stepper target pos: {0}".format(
                self.stepper_target_pos))
            log.debug("Stepper pos: {0}".format(self.stepper_pos))
            self.stepper_target_pos = self.stepper_pos + stepper_offset
            self.wait_for_stepper()

        self.log_positions()
        self.stand_by()

    def wait_for_stepper(self):
        while self.stepper_pos != self._stepper_dest:
            time.sleep(0.1)
            self.log_positions()

    def log_offset(self):
        log.debug("Difference (encoder): {0}".format(self.offset))

    @property
    def offset(self):
        return self._encoder_dest - self.encoder_pos

    @property
    def stepper_target_pos(self):
        return self.stepper.getTargetPosition(self.motor_id)

    @stepper_target_pos.setter
    def stepper_target_pos(self, val):
        self._stepper_dest = int(val)
        self.stepper.setTargetPosition(self.motor_id, int(val))

    @property
    def stepper_pos(self):
        return self.stepper.getCurrentPosition(self.motor_id)

    @stepper_pos.setter
    def stepper_pos(self, val):
        self.stepper.setCurrentPosition(self.motor_id, int(val))

    @property
    def encoder_pos(self):
        return self.encoder.getPosition(self.motor_id)

    def raw_stepper_position(self, angle):
        return round(angle * self.s / 360)

    def raw_encoder_position(self, angle):
        return round(angle * self.e / 360)

    def wake_up(self, motor_id=0):
        self.stepper.setEngaged(motor_id, 1)

    def stand_by(self, motor_id=0):
        self.stepper.setEngaged(motor_id, 0)

    def reset_positions(self, motor_id=0):
        self.stepper.setCurrentPosition(motor_id, 0)
        self.encoder.setPosition(motor_id, 0)

    def log_positions(self, motor_id=0):
        log.info("Stepper position: {0}\nEncoder position:{1}".format(
            self.stepper_pos / self.s * 360, self.encoder_pos / self.e * 360))
Ejemplo n.º 3
0
class PhidgetNode(object):
    """Node for polling InterfaceKit, Encoder Board, and Wheatstone Bridge"""
    def __init__(self):
        """Open InterfaceKit and Encoder devices and initialize ros node"""
        rospy.init_node("phidgets_node")
        #rospy.init_node("phidgets_node", log_level=rospy.DEBUG)

        # Call base class initializer, which starts a ros node with a name and log_level
        # It then opens and attaches Phidget device
        self.interfaceKit = InterfaceKit()
        self.encoder = Encoder()
        # self.bridge = Bridge()

        # initialize this to nan to indicate we haven't homed the location
        self.sled_pos = float('nan')  # position of sled in millimeters
        self.encoder_rear_offset = 0
        self.encoder_front_offset = 0

        # Open the devices and wait for them to attach
        self._attachPhidget(self.interfaceKit, "Interface Kit")
        self._attachPhidget(self.encoder, "Encoder board")
        # self._attachPhidget(self.bridge, "Wheatstone Bridge")

        self.params = rospy.get_param("/phidgets")
        self.sled_params = self.params["sled"]

        self.sensor_pub = rospy.Publisher("/actuator_states/raw/",
                                          ActuatorStates,
                                          queue_size=10)
        self.sensor_processed_pub = rospy.Publisher("/actuator_states/proc",
                                                    ActuatorStatesProcessed,
                                                    queue_size=10)
        self.limit_sub = rospy.Subscriber("/pololu/limit_switch",
                                          LimitSwitch,
                                          self.limit_callback,
                                          queue_size=10)
        self.sled_is_homed = False

        # enable both the sled encoders
        self.encoder.setEnabled(self.sled_params["encoder_index"]["left"],
                                True)
        self.encoder.setEnabled(self.sled_params["encoder_index"]["right"],
                                True)

        # Display info of the devices
        self._displayDeviceInfo(self.encoder)
        self.displayInterfaceKitInfo()
        # self.displayBridgeInfo()

    def _attachPhidget(self, phidget, name):
        """Open and wait for the Phidget object to attach"""
        try:
            phidget.openPhidget()
            rospy.loginfo("Waiting for %s to attach....", name)
            phidget.waitForAttach(10000)  # 10 s
        except:
            rospy.logerr("%s did not attach!", name)

    def _displayDeviceInfo(self, phidget):
        """Display relevant info about device"""
        rospy.logdebug("Attached: %s", phidget.isAttached())
        rospy.logdebug("Type: %s", phidget.getDeviceName())
        rospy.logdebug("Serial No.: %s", phidget.getSerialNum())
        rospy.logdebug("Version: %s", phidget.getDeviceVersion())

    def displayInterfaceKitInfo(self):
        """Display relevant info about interface kit"""
        self._displayDeviceInfo(self.interfaceKit)
        rospy.logdebug("Number of Digital Inputs: %i",
                       self.interfaceKit.getInputCount())
        rospy.logdebug("Number of Digital Outputs: %i",
                       self.interfaceKit.getOutputCount())
        rospy.logdebug("Number of Sensor Inputs: %i",
                       self.interfaceKit.getSensorCount())
        rospy.logdebug("Min data rate: %i, Max data rate: %i",
                       self.interfaceKit.getDataRateMin(0),
                       self.interfaceKit.getDataRateMax(0))

    def displayBridgeInfo(self):
        """Display relevant info about wheatstone bridge"""
        self._displayDeviceInfo(self.bridge)
        rospy.logdebug("Number of bridge inputs: %i",
                       self.bridge.getInputCount())
        rospy.logdebug("Data Rate Max: %d", self.bridge.getDataRateMax())
        rospy.logdebug("Data Rate Min: %d", self.bridge.getDataRateMin())
        rospy.logdebug("Input Value Max: %d", self.bridge.getBridgeMax(0))
        rospy.logdebug("Input Value Min: %d", self.bridge.getBridgeMin(0))

    def limit_callback(self, limit_switch):
        """Callback for the current state of the switches. If this is the first
        time they have been pressed, set flag to indicate the sled has been homed"""
        if limit_switch.rear and not self.sled_is_homed:
            self.sled_pos = self.sled_params["rear_pos"]
            self.encoder_rear_offset = self.pollEncoders()
            self.sled_is_homed = True
        #elif limit_switch.front:
        #    self.sled_pos = self.sled_params["front_pos"]
        #    self.encoder_front_offset = self.pollEncoders()
        #    self.sled_is_homed = True

    def pollEncoders(self):
        """Return averaged value of sled left and right encoder values"""
        sled_left_ticks = self.encoder.getPosition(
            self.sled_params["encoder_index"]["left"])
        sled_right_ticks = self.encoder.getPosition(
            self.sled_params["encoder_index"]["right"])
        average = (-sled_left_ticks + sled_right_ticks) / 2
        rospy.logdebug("left right tick average:  %.2f", average)
        return average

    def pollLinearActuator(self, name):
        """Poll the sensor for its raw value. Then convert that value
        to the actual position in millimeters. Return as a SensorValuePair"""
        device = self.params[name]
        index = device["sensor_index"]
        min_pot_val = device["min"]
        max_pot_val = device["max"]
        physical_length = device["length"]
        # Create objects to fill and later send over message
        raw_val = self.interfaceKit.getSensorValue(index)
        # Convert the potentiometer value to millimeter position
        pos = (raw_val - min_pot_val) * (physical_length /
                                         (max_pot_val - min_pot_val))

        return pos

    def sendProcessedMessage(self, actuator_states):
        """Process the interface sensor message to average and get the positions
        of the motors"""
        proc = ActuatorStatesProcessed()
        proc.header = actuator_states.header
        proc.arm = (actuator_states.arm_left + actuator_states.arm_right) / 2
        proc.bucket = (actuator_states.bucket_left +
                       actuator_states.bucket_right) / 2
        proc.sled = actuator_states.sled

        self.sensor_processed_pub.publish(proc)

    def pollSensors(self):
        """Poll and publish all the sensor values as ActuatorStates message"""
        ticks_per_mm = self.sled_params["ticks_per_mm"]

        actuator_states = ActuatorStates(
        )  # create object to store message components to send on topic
        header = Header()

        actuator_states.header.stamp = rospy.Time.now()
        actuator_states.arm_left = self.pollLinearActuator("arm_left")
        actuator_states.arm_right = self.pollLinearActuator("arm_right")
        actuator_states.bucket_left = self.pollLinearActuator("bucket_left")
        actuator_states.bucket_right = self.pollLinearActuator("bucket_right")

        sled_left_ticks = self.encoder.getPosition(
            self.sled_params["encoder_index"]["left"])
        sled_right_ticks = self.encoder.getPosition(
            self.sled_params["encoder_index"]["right"])

        rospy.logdebug("left ticks = %d", sled_left_ticks)
        rospy.logdebug("right ticks = %d", sled_right_ticks)

        if self.sled_is_homed:
            rospy.logdebug("left pos = %.2f", sled_left_ticks / ticks_per_mm)
            rospy.logdebug("right pos = %.2f", sled_right_ticks / ticks_per_mm)
            sled_pos = (self.pollEncoders() -
                        self.encoder_rear_offset) / ticks_per_mm
        else:
            sled_pos = float(
                'nan'
            )  # set this so everybody knows we haven't homed the sled yet

        actuator_states.sled = sled_pos
        # TODO: add sled_pos to position when that is setup

        # actuator_states.sled = self.interfaceKit.getSensorValue(self.params["sled"]["sensor_index"])
        self.sensor_pub.publish(actuator_states)
        self.sendProcessedMessage(actuator_states)

    def run(self):
        """Run the main ros loop"""
        rospy.loginfo("Starting phidgets node loop")
        r_time = rospy.Rate(10)  #10 Hz looping
        while not rospy.is_shutdown():
            self.pollSensors()
            r_time.sleep()
Ejemplo n.º 4
0
    print "message", e.message
    
    raise

if motorControl.isAttached():
    print "motor control attached"
else:
    print "motor attach Failed"

encoder.setPosition(whichEncoder, 0)
encoder.setEnabled(whichEncoder, True)
averageAcceleration = (motorControl.getAccelerationMax(whichMotor) - motorControl.getAccelerationMin(whichMotor)) * 0.9
print "averageAcceleration", averageAcceleration
motorControl.setAcceleration(whichMotor, averageAcceleration)

position = encoder.getPosition(whichEncoder)
print "Starting position", position
startTime = time.clock()
motorControl.setVelocity(whichMotor, velocity)
try:
    while position < pulsesWanted:
        position = encoder.getPosition(whichEncoder) * encoderSign
        print "Position", position, "Current", motorControl.getCurrent(whichMotor), "\r",
    
except:
    print "Aborted"
    pass
    
endTime = time.clock()
motorControl.setVelocity(whichMotor, 0)
}

# Convert speed: deg/sec -> RPM -> % duty cycle. Motor rotation: 33 RPM at 100% voltage (24 VDC)
# Convert position to counts for the encoder. Encoder has 300 counts/revolution = 0.833 counts/degree with a 76 reduction ratio
PWM_duty_cycle = []
commanded_position = []

# each list ordered [open, close]
for key, value in inputs.items():
	PWM_duty_cycle.append((value[0] * 0.16666) / 33)
	commanded_position.append(value[1] * (0.83333/76)

# Set encoder & calculate position difference [open, close]
encoder.setPosition(0, 0)
position_difference = []
position_difference[:] = [i - encoder.getPosition(0) for i in commanded_position]

# Create simple PID controller
# Reference: http://examples.oreilly.com/9780596809577/CH09/PID.py
class PID:
	"""
	Simple PID control from "Real-World Instrumentation with Python" 
	by J. M. Hughes, published by O'Reilly Media, December 2010"
	"""
	 def __init__(self):
        # initialze gains
        self.Kp = 0
        self.Kd = 0
        self.Ki = 0

        self.Initialize()