Mercurial > pkubik
changeset 3:f0c05b8506be
simple creep gait
| author | Radomir Dopieralski <sheep-devel@sheep.art.pl> |
|---|---|
| date | Mon, 24 Nov 2014 00:35:13 +0100 |
| parents | ca16b2fe3eb3 |
| children | 24c33455723d |
| files | controller/servos.ino creep.py robot.py |
| diffstat | 3 files changed, 279 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/controller/servos.ino Thu Nov 20 00:10:37 2014 +0100 +++ b/controller/servos.ino Mon Nov 24 00:35:13 2014 +0100 @@ -4,7 +4,7 @@ // Range of the servos. #define SERVO_FREQ_MIN 300 -#define SERVO_FREQ_MAX 1200 +#define SERVO_FREQ_MAX 1300 static const unsigned char SERVO_PINS[SERVOS] = { // ankle, knee, hip @@ -15,10 +15,10 @@ }; static const char SERVO_REVERSE[SERVOS] = { // ankle, knee, hip - -1, -1, -1, // front left + -1, -1, 1, // front left 1, 1, 1, // hind left -1, -1, -1, // hind right - 1, 1, 1 // front right + 1, 1, -1 // front right }; static const char SERVO_TRIM[SERVOS] = { // ankle, knee, hip
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/creep.py Mon Nov 24 00:35:13 2014 +0100 @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import itertools +import time + +import robot + + +class Creep(robot.Robot): + step_height = -2.0 # How high to raise the legs. + stride = 13.0 # How long steps to take. + swing = 6, 6 # How much to shift the body x and y. + swing_speed = 3 # How fast to shift the body. + posture = 8, 4 # How much to change posture depending on direction. + gaits = { + 'forward': {None: 1, 1: 0, 0: 2, 2: 3, 3: 1}, + 'backward': {None: 1, 1: 3, 3: 2, 2: 0, 0: 1}, + 'left': {None: 0, 0: 1, 1: 2, 2: 3, 3: 0}, + 'right': {None: 0, 0: 3, 3: 2, 2: 1, 1: 0}, + } + + def __init__(self, serial): + super(Creep, self).__init__(serial) + self.robot = self + self.on_ground = set(self.legs) + self.speed = [0.0, 1.0] + self.rotation = 0.0 + self.gait = 'forward' + self.shift[2] = 5 + + def _raise_leg(self, leg): + """Raises the leg off the ground.""" + self.on_ground.remove(leg) + leg.move_to((None, None, self.step_height)) + for frame in range(2): + yield + + def _lower_leg(self, leg): + """Puts the leg on the ground.""" + leg.move_to((None, None, leg.home[2] + 1)) + for frame in range(2): + yield + leg.move_to((None, None, leg.home[2])) + self.on_ground.add(leg) + + def _advance_leg(self, leg): + """Moves the leg forward.""" + leg.move_to(( + ( + leg.home[0] - self.stride * self.speed[0] * leg.x_dir - + self.posture[0] * abs(self.speed[0]) + ), + ( + leg.home[1] + self.stride * self.speed[1] + + leg.y_dir * self.posture[1] * abs(self.speed[1]) + ), + None, + )) + leg.rotate_by(self.rotation * leg.x_dir * leg.y_dir * 10) + for frame in range(4): + yield + + def _shift_body(self, shift): + """Shifts the body to the specified position.""" + shift = list(shift) + while self.robot.shift[:2] != shift: + for i in range(2): + if self.robot.shift[i] > shift[i]: + self.robot.shift[i] -= self.swing_speed + elif self.robot.shift[i] < shift[i]: + self.robot.shift[i] += self.swing_speed + yield + + def stomp_anim(self): + """This animation is for making steps and shifting the body.""" + leg_index = None + while True: + while not self.gait: + # Don't raise the legs if not moving. + try: + yield self._shift_body((0, 0)).next() + except StopIteration: + yield + leg_index = self.gaits[self.gait][leg_index] + leg = self.robot.legs[leg_index] + for frame in self._shift_body((leg.x_dir * self.swing[0], + -leg.y_dir * self.swing[1])): + yield + for frame in self._raise_leg(leg): + yield + for frame in self._advance_leg(leg): + yield + for frame in self._lower_leg(leg): + yield + + def retract_anim(self): + """This animation is for moving the legs on the ground. + + They should all move in the opposite direction than the speed. + """ + while True: + for leg in self.on_ground: + leg.move_by((self.speed[0] * 0.5 * leg.x_dir, + -self.speed[1], 0)) + if self.rotation: + leg.rotate_by(-self.rotation * leg.x_dir * leg.y_dir) + yield + + def run(self): + for (_1, _2) in itertools.izip(self.stomp_anim(), self.retract_anim()): + self.update() + time.sleep(0.125) + + +if __name__ == '__main__': + import serial + + Creep(robot.ServoController(serial.Serial('/dev/ttyUSB0', 57600))).run()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/robot.py Mon Nov 24 00:35:13 2014 +0100 @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import math +import struct + + +SQRT2 = math.sqrt(2) + + +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 ServoController(object): + """Send data to the servo controller.""" + + def __init__(self, serial): + self.positions = [0] * 12 + self.serial = serial + + def update(self): + format = '>B' + 'h' * 12 + command = 0xff + positions = [int(pos * 1000) for pos in self.positions] + packet = struct.pack(format, command, *positions) + self.serial.write(packet) + + def move_servo(self, servo, radians=None, degrees=None): + if degrees is not None: + radians = math.radians(degrees) + if radians is not None: + self.positions[servo] = radians + + +class Leg(object): + def __init__(self, robot, index): + self.robot = robot + self.index = index + self.x_dir, self.y_dir = robot.leg_order[index] + self.home = ( + (robot.coxa + robot.femur) / SQRT2, + -(robot.coxa + robot.femur) / SQRT2 * self.y_dir, + -robot.tibia, + ) + + def move_to(self, position): + self.robot.move_leg_to(self.index, position) + + def move_by(self, offset): + self.robot.move_leg_by(self.index, offset) + + def rotate_by(self, degrees): + self.robot.rotate_leg_by(self.index, degrees) + + +class Robot(object): + base = 17.5 + coxa = 8.0 + femur = 20.5 + tibia = 24.5 + leg_order = [(1, 1), (1, -1), (-1, -1), (-1, 1)] + + def __init__(self, servos): + self.servos = servos + self.legs = [Leg(self, index) for index in range(4)] + self.leg_positions = [leg.home for leg in self.legs] + self.shift = [0, 0, 0] + + def _inverse_kinematics(self, (x, y, z), (dx, dy)): + """Calculate the 3 angles of the leg, in degrees.""" + 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) + knee = -math.degrees(knee) + ankle = math.degrees(ankle) - 90 - 45 + hip = -math.degrees(hip) - 45 * dy + return ankle, knee, hip + + def update(self): + for leg, position in enumerate(self.leg_positions): + pos = [ + position[0] + self.shift[0] * self.leg_order[leg][0], + position[1] + self.shift[1], + position[2] + self.shift[2], + ] + angles = self._inverse_kinematics(pos, self.leg_order[leg]) + for joint, angle in enumerate(angles): + self.servos.move_servo(leg * 3 + joint, degrees=angle) + self.servos.update() + + def move_leg_to(self, leg, position): + self.leg_positions[leg] = [ + new if new is not None else old + for (new, old) in zip(position, self.leg_positions[leg]) + ] + + def move_leg_by(self, leg, offset): + self.leg_positions[leg] = [ + old + d + for (d, old) in zip(offset, self.leg_positions[leg]) + ] + + def rotate_leg_by(self, leg, degrees): + y_dir = self.leg_order[leg][1] + radians = math.radians(degrees) + x, y, z = self.leg_positions[leg] + bx, by = self.base + x, self.base - y * y_dir + r = _norm(bx, by) + a = math.atan2(bx, by) + rx = r * math.sin(a + radians) - self.base + ry = r * math.cos(a + radians) - self.base + self.move_leg_to(leg, (rx, -ry * y_dir, None)) + + +if __name__ == '__main__': + import serial + import time + + servos = ServoController(serial.Serial('/dev/ttyUSB0', 57600)) + robot = Robot(servos) + robot.update() + time.sleep(1) + steps = 10 + while True: + for frame in range(steps): + time.sleep(0.1) + for leg in robot.legs: + leg.move_by((1 * leg.x_dir, 0, 0)) + robot.update() + for frame in range(steps): + time.sleep(0.1) + for leg in robot.legs: + leg.move_by((0, 1, 0)) + robot.update() + for frame in range(steps): + time.sleep(0.1) + for leg in robot.legs: + leg.move_by((-1 * leg.x_dir, 0, 0)) + robot.update() + for frame in range(steps): + time.sleep(0.1) + for leg in robot.legs: + leg.move_by((0, -1, 0)) + robot.update()
