Mercurial > henk
changeset 9:d220325617b5
Leg inverse kinematics
| author | Radomir Dopieralski <sheep-devel@sheep.art.pl> |
|---|---|
| date | Tue, 20 Jan 2015 01:50:14 +0100 |
| parents | 6c6742b10e92 |
| children | f43f0ee169ce |
| files | leg.py |
| diffstat | 1 files changed, 60 insertions(+), 9 deletions(-) [+] |
line wrap: on
line diff
--- a/leg.py Tue Jan 20 01:49:59 2015 +0100 +++ b/leg.py Tue Jan 20 01:50:14 2015 +0100 @@ -1,22 +1,73 @@ import math -class Leg(object): - body = 36.0 +US_MIN = 600 +US_MAX = 2400 +US_CENTER = US_MIN + (US_MAX - US_MIN) // 2 +US_PER_RADIAN = float(US_MAX - US_MIN) / math.pi + + +def _solve_triangle(a, b, c): + """Calculate an angle of a triangle using the law of cosines.""" + a, b, c = abs(a), abs(b), abs(c) + if a + b < c or a + c < b or b + c < a: + raise ValueError("Impossible triangle: %r, %r, %r" % (a, b, c)) + cos = float(a ** 2 + b ** 2 - c ** 2) / (2 * a * b) + return math.acos(cos) + + +def _norm(a, b): + """Calculate the length of a vector.""" + return math.sqrt(a ** 2 + b ** 2) + + +class Leg: + body_radius = 36.0 coxa = 33.0 femur = 43.0 tibia = 53.0 - def __init__(self, servos, angle): - angle = math.radians(angle) - self.servos = servos + def __init__(self, servos, angle=0, trims=(0, 0, 0)): self.angle = angle + self.servos = servos + self.trims = trims self.base = ( - self.body * math.cos(angle), - self.body * math.sin(angle) + self.body_radius * math.cos(self.angle), + self.body_radius * math.sin(self.angle) ) self.home = ( - (self.coxa + self.femur) * math.cos(angle), - (self.coxa + self.femur) * math.sin(angle), + (self.coxa + self.femur) * math.cos(self.angle), + (self.coxa + self.femur) * math.sin(self.angle), -self.tibia, ) + self.position = [0, 0, 0] + self.move_to(*self.home) + + def _inverse_kinematics(self, x, y, z): + """Calculate the 3 angles of the leg, in radians.""" + f = _norm(x, y) - self.coxa + d = min(self.femur + self.tibia, _norm(f, z)) + hip = math.atan2(y, x) + knee = _solve_triangle(self.femur, d, self.tibia) - math.atan2(-z, f) + ankle = _solve_triangle(self.femur, self.tibia, d) + return ankle, knee, hip + + def move_to(self, x=None, y=None, z=None): + x, y, z = [given if given is not None else current + for (given, current) in zip((x, y, z), self.position)] + angles = list(self._inverse_kinematics(x, y, z)) + angles[0] += self.angle + self.angles = angles + self.position = x, y ,z + + def move_by(self, dx, dy, dz): + x, y, z = self.position + self.move_to(x + dx, y + dy, z + dz) + + def update(self, controller): + for servo, angle, trim in zip(self.servos, self.angles, self.trims): + while angle > math.pi: + angle -= math.pi * 2 + us = US_CENTER + angle * US_PER_RADIAN + trim + target = min(US_MAX * 4, max(US_MIN * 4, int(us * 4))) + controller.set_target(servo, target)
