0

I'm creating the "framework" to control a Hexapod. So (simplifying) I have a Servo class:

class Servo(object):
...
        def setAngle(self, angle):
                ##Executes order to move servo to specified angle
                ##Returns False if not possible or True if OK
                offsetAngle=self.offset+angle
                if not self.checkServoAngle(offsetAngle):
                        #Angle not within servo range
                        return=False
                else:
                        pwmvalue=self._convAngleToPWM(offsetAngle)
                        self._setPWM(pwmvalue)
                        self.angle=value
                        return=True
...

and a child HexBone class:

class HexBone(Servo):
    ## Servo.__init__ override:
    def __init__(self, I2C_ADDRESS, channel, length, startAngle, reversed=False, minAngle=NULL, maxAngle):       
        self = Servo(I2C_ADDRESS, channel, reversed, minAngle, maxAngle)

        #Setting bone length
        self.length=length
        #Positions bone in Starting Position 
        self.setAngle(startAngle)

and also a HexLimb class:

class HexLimb(object):
    def __init__(self, I2C_ADDRESS, femurLength, tibiaLength, femurInv, tibiaInv):
        #Setting precision of Limb Positioning
        self.precision=1

        #Setting i2c address and servo channels
        self.femur = HexBone(I2C_ADDRESS, 1, femurLength, 45, femurInv, 5, 190)
        self.tibia = HexBone(I2C_ADDRESS, 2, tibiaLength, 90, tibiaInv, 5, 190)

    def calcPosition(self):
        L1=self.femur.length
        L2=self.tibia.length
        try:
            a1=90-self.femur.angle#########!!!!!!
            a2=180-self.tibia.angle
            self.x=L1*math.cos(math.radians(a1))+L2*math.cos(math.radians(a1-a2))
            self.y=L1*math.sin(math.radians(a1))+L2*math.sin(math.radians(a1-a2))
        except:
            return False
        else:
            return True

In the HexLimb class whenever I do: self.femur.setAngle(30) I want to call self.calcPosition() to recalculate the limb's tip position.

I've been searching all over and couldn't found any answer...Am I doing it the wrong way?

(edited accordingly comments bellow)

liu
  • 73
  • 7
  • You're not doing `self.femur.setAngle(30)` anywhere in the `HexLimb` class, and `HexLimb` doesn't inherit from any of the other classes - so I don't quite get your question. – Lukas Graf Dec 27 '13 at 16:44
  • Also, you have a comment `#Setting limb's lengths` before setting `HexBone.length` - so a `HexBone` is a limb? If so, what's the point of `HexLimb`? – Lukas Graf Dec 27 '13 at 16:48
  • Thanks for your reply. I still haven't finished creating the all class (that's why I don't have self.femur.setAngle(30), also the class is incomplete since its much complex. – liu Dec 27 '13 at 17:55
  • You're welcome. Because this could involve quite a lot of forth and back, I'd be happy to help you in [**chat**](http://chat.stackoverflow.com/rooms/44009/hexapod) if you'd like - it's just that I'm confused about how you want your framework to look like and to be used. – Lukas Graf Dec 27 '13 at 18:00
  • I've edited the code. A limb is composed of several bones (i've corrected the comments) Calling Servo instance method setAngle, I pretend to recalculate all positions running HexLimb.calcPosition – liu Dec 27 '13 at 18:03
  • Sorry, im new to this. I don't have enough reputation for chat reply:( – liu Dec 27 '13 at 18:10
  • Oh, I didn't realize you need rep for that, sorry. I guess my questions boil down to: How would you like to use your framework once it's finished? Can you add an exmaple of how you would instanciate the classes, and how you would use them to control the hexapod? – Lukas Graf Dec 27 '13 at 18:16
  • Specifically: Which ones method's do you intend to use directly (like `thing.walkForward()`, and which ones will be internal methods that are called by the objects themselves (on each other)? – Lukas Graf Dec 27 '13 at 18:18
  • And where would you be calling `femur.setAngle(30)` from? – Lukas Graf Dec 27 '13 at 18:20
  • I'm using inverse kinematics algorithm that iterates small angular changes of both servos to reach final position, that's why im not posting all HexLimb class. Instead let's assume a method: – liu Dec 27 '13 at 19:37
  • I'm using inverse kinematics algorithm that iterates small angular changes of both servos to reach final position, that's why im not posting all HexLimb class. Instead let's assume a method #liftLimb(self): self.femur.setAngle(30) besides setting the angle i want to automatically call the method calcPosition without explicitly calling it everytime i "setAngle" – liu Dec 27 '13 at 19:43

2 Answers2

1

Your HexLimb needs to know about the HexBones attached to it to calculate its position. But the HexBones, being Servos, also need to know about the HexLimb they are attached to so that they can trigger a re-calculation of the limb's position.

One solution is to keep a back-reference on the HexBones to the HexLimb they're attached to.

In this example, I create a back-reference called limb in HexLimb.__init__() on both bones - you could also call it parent to be more generic about it.

from random import random

class Servo(object):
    """Servo controller"""
    def __init__(self):
        self.angle = 0

    def set_angle(self, angle):
        print "Setting angle to %s" % angle
        self.angle = angle
        # Let limb recalculate its position
        self.limb.calc_position()


class HexBone(Servo):
    """A bone that can be attached to a limb and moved."""
    def __init__(self, length):
        super(HexBone, self).__init__()
        self.length = length

        # Will be assigned later when attached to a limb
        self.limb = None


class HexLimb(object):
    """A limb consisting of several bones."""
    def __init__(self):
        self.femur = HexBone(42)
        self.femur.limb = self

        self.tibia = HexBone(30)
        self.tibia.limb = self

    def calc_position(self):
        print "Calculating position..."
        # Something that needs self.femur and self.tibia
        self.x = self.femur.length * random()
        self.y = self.tibia.length * random()

    def extend(self):
        self.tibia.set_angle(0)    # extend knee


left_leg = HexLimb()
left_leg.extend()
Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
Lukas Graf
  • 30,317
  • 8
  • 77
  • 92
  • That solves the question but isnt there a way of mantaining Servo class intact and more "general". Because if i try to use it elsewhere ill get an error because limb is not set... – liu Dec 27 '13 at 20:23
  • First of all, sorry for the late reply. I think your version with a callback function that may or may not be passed is the way to go, I would have suggested something along those lines. A totally different route to go would be to use an [event based system](http://stackoverflow.com/q/1092531/1599111) (or implement a simple one yourself). `Servo.set_angle()` could then fire an `AngleChangedEvent` (with a `limb` argument), and `HexLimbs` could subscribe to these events. – Lukas Graf Jan 03 '14 at 15:50
0

Taking what Lukas Graf suggested i've changed the code a little bit in order to maintain the Servo class more general. Basically Servo (and inherently its child class HexBone) take a callback function reference as an optional argument for init (which defaults to None). When calling Servo.setAngle in the end this will execute the callback function. Code is as follows:

from random import random


class Servo(object):
    """Servo controller"""
    def __init__(self, callback=None): #Servo takes a callback function as argument
        self.angle = 0
        self.callback=callback

    def set_angle(self, angle):
        print("Setting angle to %s" % angle)
        self.angle = angle
        # After angle set executes the callback function
        if self.callback is not None: self.callback()


class HexBone(Servo):
    """A bone that can be attached to a limb and moved."""
    def __init__(self, length, callback):
        super(HexBone, self).__init__(callback)
        self.length = length


class HexLimb(object):
    """A limb consisting of several bones."""
    def __init__(self):
        self.femur = HexBone(42, self.calc_position)
        self.femur.limb = self

        self.tibia = HexBone(30, self.calc_position)
        self.tibia.limb = self

    def calc_position(self):
        print("Calculating position...")
        # Something that needs self.femur and self.tibia
        self.x = self.femur.length * random()
        self.y = self.tibia.length * random()

    def extend(self):
        self.tibia.set_angle(0)    # extend knee


left_leg = HexLimb()
left_leg.extend()
BenMorel
  • 34,448
  • 50
  • 182
  • 322
liu
  • 73
  • 7