├── parts ├── stl │ ├── foot.stl │ ├── legA.stl │ ├── legB.stl │ ├── bodyA.stl │ ├── bodyB.stl │ └── bracket.stl └── freecad │ └── kame.fcstd ├── doc ├── images │ ├── by-sa.png │ ├── kame.jpg │ ├── main.png │ └── bq-logo-human-right-technology.png └── data.json ├── code ├── main.py ├── teleop.py └── kame.py └── README.md /parts/stl/foot.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/kame/HEAD/parts/stl/foot.stl -------------------------------------------------------------------------------- /parts/stl/legA.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/kame/HEAD/parts/stl/legA.stl -------------------------------------------------------------------------------- /parts/stl/legB.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/kame/HEAD/parts/stl/legB.stl -------------------------------------------------------------------------------- /doc/images/by-sa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/kame/HEAD/doc/images/by-sa.png -------------------------------------------------------------------------------- /doc/images/kame.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/kame/HEAD/doc/images/kame.jpg -------------------------------------------------------------------------------- /doc/images/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/kame/HEAD/doc/images/main.png -------------------------------------------------------------------------------- /parts/stl/bodyA.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/kame/HEAD/parts/stl/bodyA.stl -------------------------------------------------------------------------------- /parts/stl/bodyB.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/kame/HEAD/parts/stl/bodyB.stl -------------------------------------------------------------------------------- /parts/stl/bracket.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/kame/HEAD/parts/stl/bracket.stl -------------------------------------------------------------------------------- /parts/freecad/kame.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/kame/HEAD/parts/freecad/kame.fcstd -------------------------------------------------------------------------------- /doc/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "doc/images/main.png", 3 | "tags": ["robotics", "hardware", "software"] 4 | } 5 | -------------------------------------------------------------------------------- /doc/images/bq-logo-human-right-technology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bqlabs/kame/HEAD/doc/images/bq-logo-human-right-technology.png -------------------------------------------------------------------------------- /code/main.py: -------------------------------------------------------------------------------- 1 | from kame import Kame 2 | 3 | robot = Kame([-5, -14, 4, 20, -10, 0, 12, 5], [3, 2, 15, 12, 1, 0, 14, 13]) 4 | 5 | # robot.zero() 6 | # robot.jump() 7 | # robot.turnR(4) 8 | robot.kickL() 9 | 10 | 11 | 12 | # robot.turnL(4) 13 | # robot.back(20) 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # KAME 6 | 7 | Kame is an experimental quadruped walker with PLM legs. The main controller of this robot is a Raspberry Pi 2. Main code for locomotion and gaits is written in Python using oscillator algorithms. 8 | 9 |

10 | 11 |

12 | 13 | # License 14 | 15 |

16 | 17 |

18 | -------------------------------------------------------------------------------- /code/teleop.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | sys.path.append(os.path.dirname(os.path.abspath(__file__))) 4 | 5 | import pygame 6 | import time 7 | from kame import Kame 8 | robot = Kame([-5, -14, 4, 20, -10, 0, 12, 5], [3, 2, 15, 12, 1, 0, 14, 13]) 9 | 10 | pygame.init() 11 | clock = pygame.time.Clock() 12 | pygame.joystick.init() 13 | joystick = pygame.joystick.Joystick(0) 14 | joystick.init() 15 | 16 | 17 | init_ref = time.time() 18 | g_phase = 0 19 | T = 350.0 20 | 21 | while True: 22 | for event in pygame.event.get(): 23 | pass 24 | 25 | axis_ax = joystick.get_axis(0) 26 | axis_ay = joystick.get_axis(1) 27 | inclination = [joystick.get_axis(2), joystick.get_axis(3)] 28 | cross = joystick.get_hat(0) 29 | a_button = joystick.get_button(2) 30 | left_trigger = joystick.get_button(6) 31 | right_trigger = joystick.get_button(7) 32 | 33 | 34 | if axis_ax == 0 and axis_ay == 0: 35 | g_phase = 0 36 | 37 | if a_button == 1: 38 | robot.jump() 39 | 40 | elif cross[1] == 1: 41 | print 'walk' 42 | robot.walk(1, T) 43 | 44 | elif cross[1] == -1: 45 | print 'back' 46 | robot.back(1, T) 47 | 48 | elif cross[0] == 1: 49 | print 'turnR' 50 | robot.turnR(1, T) 51 | 52 | elif cross[0] == -1: 53 | print 'turnL' 54 | robot.turnL(1, T) 55 | else: 56 | robot.home() 57 | 58 | else: 59 | g_phase += robot.omnimove(g_phase, inclination, axis_ax, axis_ay) 60 | 61 | clock.tick(200) 62 | 63 | pygame.quit() 64 | -------------------------------------------------------------------------------- /code/kame.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | sys.path.append(os.path.abspath(__file__).replace('kame/code/kame.py', '') + 'pybotics/') 4 | 5 | import time 6 | import numpy as np 7 | import math 8 | import smbus 9 | from driver.pca9685.pca9685 import ServoController 10 | import control.octosnake.octosnake as octosnake 11 | 12 | # Servos: 13 | # _________ ________ _________ 14 | # |(2)______)(0) (1)(______(3)| 15 | # |__| | KAME | |__| 16 | # | | 17 | # | | 18 | # | | 19 | # _________ | | _________ 20 | # |(6)______)(4)______(5)(______(7)| 21 | # |__| |__| 22 | # /\ 23 | # | 24 | # USBs | 25 | 26 | 27 | class Kame(object): 28 | 29 | def __init__(self, servo_trims, servo_pins, i2c_bus=1, pca9685_address=0x40, name='kame'): 30 | 31 | # Configuration 32 | self._name = name 33 | self._i2c_bus = i2c_bus 34 | self._servo_trims = servo_trims 35 | self._servo_pins = servo_pins 36 | self._pca9685_address = pca9685_address 37 | 38 | # Setting up hardware 39 | self._bus = smbus.SMBus(self._i2c_bus) 40 | if not self._bus: 41 | raise Exception('I2C bus connection failed!') 42 | 43 | self.controller = ServoController(self._bus, self._pca9685_address) 44 | 45 | # Setting up OctoSnake 46 | self.osc = [] 47 | self.osc.append(octosnake.Oscillator()) 48 | self.osc.append(octosnake.Oscillator()) 49 | self.osc.append(octosnake.Oscillator()) 50 | self.osc.append(octosnake.Oscillator()) 51 | self.osc.append(octosnake.Oscillator()) 52 | self.osc.append(octosnake.Oscillator()) 53 | self.osc.append(octosnake.Oscillator()) 54 | self.osc.append(octosnake.Oscillator()) 55 | 56 | self.osc[1].ref_time = self.osc[0].ref_time 57 | self.osc[2].ref_time = self.osc[0].ref_time 58 | self.osc[3].ref_time = self.osc[0].ref_time 59 | self.osc[4].ref_time = self.osc[0].ref_time 60 | self.osc[5].ref_time = self.osc[0].ref_time 61 | self.osc[6].ref_time = self.osc[0].ref_time 62 | self.osc[7].ref_time = self.osc[0].ref_time 63 | 64 | # Setting up servo controller 65 | for i in range(len(self._servo_pins)): 66 | self.controller.addServo(self._servo_pins[i], self._servo_trims[i]) 67 | 68 | self.controller.servos[self._servo_pins[1]].reverse = True 69 | # self.controller.servos[self._servo_pins[3]].reverse = True 70 | self.controller.servos[self._servo_pins[5]].reverse = True 71 | # self.controller.servos[self._servo_pins[6]].reverse = True 72 | 73 | def back(self, steps, T=750.0): 74 | 75 | x_amp = 20 76 | z_amp = 15 77 | front_x = 8 78 | i = 0 79 | 80 | period = [T, T, T/2, T/2, T, T, T/2, T/2] 81 | amplitude = [x_amp, x_amp, z_amp, z_amp, x_amp, x_amp, z_amp, z_amp] 82 | offset = [front_x, front_x, -25, -25, front_x, front_x, -25, -25] 83 | phase = [270, 90, i, 270, 90, 270, i, i] 84 | 85 | for i in range(len(self.osc)): 86 | self.osc[i].period = period[i] 87 | self.osc[i].amplitude = amplitude[i] 88 | self.osc[i].phase = phase[i] 89 | self.osc[i].offset = offset[i] 90 | 91 | init_ref = time.time() 92 | final = init_ref + float(T*steps/1000) 93 | self.osc[0].ref_time = init_ref 94 | self.osc[1].ref_time = self.osc[0].ref_time 95 | self.osc[2].ref_time = self.osc[0].ref_time 96 | self.osc[3].ref_time = self.osc[0].ref_time 97 | self.osc[4].ref_time = self.osc[0].ref_time 98 | self.osc[5].ref_time = self.osc[0].ref_time 99 | self.osc[6].ref_time = self.osc[0].ref_time 100 | self.osc[7].ref_time = self.osc[0].ref_time 101 | while time.time() < final: 102 | side = int((time.time()-init_ref) / (T/2000.0)) % 2 103 | try: 104 | for i in range(len(self.osc)): 105 | self.osc[i].refresh() 106 | 107 | self.controller.move(self._servo_pins[0], self.osc[0].output) 108 | self.controller.move(self._servo_pins[1], self.osc[1].output) 109 | self.controller.move(self._servo_pins[4], self.osc[4].output) 110 | self.controller.move(self._servo_pins[5], self.osc[5].output) 111 | if side == 0: 112 | self.controller.move(self._servo_pins[3], -self.osc[3].output) 113 | self.controller.move(self._servo_pins[6], -self.osc[3].output) 114 | else: 115 | self.controller.move(self._servo_pins[2], self.osc[3].output) 116 | self.controller.move(self._servo_pins[7], self.osc[3].output) 117 | 118 | except IOError: 119 | self._bus = smbus.SMBus(self._i2c_bus) 120 | 121 | def walk(self, steps, T=450.0): 122 | 123 | x_amp = 20 124 | z_amp = 15 125 | front_x = -12 126 | period = [T, T, T/2, T/2, T, T, T/2, T/2] 127 | amplitude = [x_amp, x_amp, z_amp, z_amp, x_amp, x_amp, z_amp, z_amp] 128 | offset = [front_x, front_x, -25, -25, front_x, front_x, -25, -25] 129 | phase = [90, 270, 90, 270, 270, 90, 270, 90] 130 | 131 | for i in range(len(self.osc)): 132 | self.osc[i].period = period[i] 133 | self.osc[i].amplitude = amplitude[i] 134 | self.osc[i].phase = phase[i] 135 | self.osc[i].offset = offset[i] 136 | 137 | init_ref = time.time() 138 | final = init_ref + float(T*steps/1000) 139 | self.osc[0].ref_time = init_ref 140 | self.osc[1].ref_time = self.osc[0].ref_time 141 | self.osc[2].ref_time = self.osc[0].ref_time 142 | self.osc[3].ref_time = self.osc[0].ref_time 143 | self.osc[4].ref_time = self.osc[0].ref_time 144 | self.osc[5].ref_time = self.osc[0].ref_time 145 | self.osc[6].ref_time = self.osc[0].ref_time 146 | self.osc[7].ref_time = self.osc[0].ref_time 147 | while time.time() < final: 148 | side = int((time.time()-init_ref) / (T/2000.0)) % 2 149 | try: 150 | for i in range(len(self.osc)): 151 | self.osc[i].refresh() 152 | 153 | self.controller.move(self._servo_pins[0], self.osc[0].output) 154 | self.controller.move(self._servo_pins[1], self.osc[1].output) 155 | if side == 0: 156 | self.controller.move(self._servo_pins[3], -self.osc[3].output) 157 | self.controller.move(self._servo_pins[6], -self.osc[3].output) 158 | else: 159 | self.controller.move(self._servo_pins[2], self.osc[3].output) 160 | self.controller.move(self._servo_pins[7], self.osc[3].output) 161 | self.controller.move(self._servo_pins[4], self.osc[4].output) 162 | self.controller.move(self._servo_pins[5], self.osc[5].output) 163 | 164 | except IOError: 165 | self._bus = smbus.SMBus(self._i2c_bus) 166 | 167 | def turnL(self, steps, T=500.0): 168 | 169 | x_amp = 15 170 | z_amp = 15 171 | period = [T, T, T, T, T, T, T, T] 172 | amplitude = [x_amp, x_amp, z_amp, z_amp, x_amp, x_amp, z_amp, z_amp] 173 | offset = [30, 30, -30, 30, -30, -30, 30, -30] 174 | phase = [0, 0, 270, 270, 180, 180, 270, 270] 175 | 176 | for i in range(len(self.osc)): 177 | self.osc[i].period = period[i] 178 | self.osc[i].amplitude = amplitude[i] 179 | self.osc[i].phase = phase[i] 180 | self.osc[i].offset = offset[i] 181 | 182 | final = time.time() + float(T*steps/1000) 183 | while time.time() < final: 184 | try: 185 | for i in range(len(self.osc)): 186 | self.osc[i].refresh() 187 | for i in range(len(self.osc)): 188 | self.controller.move(self._servo_pins[i], self.osc[i].output) 189 | 190 | except IOError: 191 | self._bus = smbus.SMBus(self._i2c_bus) 192 | 193 | def turnR(self, steps, T=500.0): 194 | 195 | x_amp = 15 196 | z_amp = 15 197 | period = [T, T, T, T, T, T, T, T] 198 | amplitude = [x_amp, x_amp, z_amp, z_amp, x_amp, x_amp, z_amp, z_amp] 199 | offset = [30, 30, -30, 30, -30, -30, 30, -30] 200 | phase = [0, 0, 90, 90, 180, 180, 90, 90] 201 | 202 | for i in range(len(self.osc)): 203 | self.osc[i].period = period[i] 204 | self.osc[i].amplitude = amplitude[i] 205 | self.osc[i].phase = phase[i] 206 | self.osc[i].offset = offset[i] 207 | 208 | final = time.time() + float(T*steps/1000) 209 | while time.time() < final: 210 | try: 211 | for i in range(len(self.osc)): 212 | self.osc[i].refresh() 213 | for i in range(len(self.osc)): 214 | self.controller.move(self._servo_pins[i], self.osc[i].output) 215 | 216 | except IOError: 217 | self._bus = smbus.SMBus(self._i2c_bus) 218 | 219 | def dance(self, steps): 220 | 221 | x_amp = 0 222 | z_amp = 30 223 | T = 500 224 | period = [T, T, T, T, T, T, T, T] 225 | amplitude = [x_amp, x_amp, z_amp, z_amp, x_amp, x_amp, z_amp, z_amp] 226 | offset = [45, 45, -30, -30, -45, -45, 30, 30] 227 | phase = [0, 0, 0, 90, 0, 0, 270, 180] 228 | 229 | for i in range(len(self.osc)): 230 | self.osc[i].period = period[i] 231 | self.osc[i].amplitude = amplitude[i] 232 | self.osc[i].phase = phase[i] 233 | self.osc[i].offset = offset[i] 234 | 235 | final = time.time() + float(T*steps/1000) 236 | while time.time() < final: 237 | try: 238 | for i in range(len(self.osc)): 239 | self.osc[i].refresh() 240 | for i in range(len(self.osc)): 241 | self.controller.move(self._servo_pins[i], self.osc[i].output) 242 | 243 | except IOError: 244 | self._bus = smbus.SMBus(self._i2c_bus) 245 | 246 | def zero(self): 247 | for i in range(len(self._servo_pins)): 248 | self.controller.move(self._servo_pins[i], 0) 249 | 250 | def home(self): 251 | self.controller.move(self._servo_pins[0], 10) 252 | self.controller.move(self._servo_pins[1], 10) 253 | self.controller.move(self._servo_pins[2], 0) 254 | self.controller.move(self._servo_pins[3], 0) 255 | self.controller.move(self._servo_pins[4], -10) 256 | self.controller.move(self._servo_pins[5], -10) 257 | self.controller.move(self._servo_pins[6], 0) 258 | self.controller.move(self._servo_pins[7], 0) 259 | 260 | def jump(self): 261 | for i in range(len(self._servo_pins)): 262 | self.controller.move(self._servo_pins[i], 0) 263 | 264 | self.controller.move(self._servo_pins[2], 10) 265 | self.controller.move(self._servo_pins[3], -10) 266 | self.controller.move(self._servo_pins[6], -10) 267 | self.controller.move(self._servo_pins[7], 10) 268 | time.sleep(0.3) 269 | 270 | amp = 50 271 | self.controller.move(self._servo_pins[2], -amp) 272 | self.controller.move(self._servo_pins[3], amp) 273 | self.controller.move(self._servo_pins[6], amp) 274 | self.controller.move(self._servo_pins[7], -amp) 275 | 276 | time.sleep(0.08) 277 | self.controller.move(self._servo_pins[2], 10) 278 | self.controller.move(self._servo_pins[3], -10) 279 | self.controller.move(self._servo_pins[6], -10) 280 | self.controller.move(self._servo_pins[7], 10) 281 | 282 | def kickL(self): 283 | self.home() 284 | self.controller.move(self._servo_pins[2], -10) 285 | delay(4) 286 | 287 | 288 | def omnimove(self, global_phase, inclination, x, y): 289 | 290 | speed = np.sqrt(x**2 + y**2) 291 | speed = 1 if speed > 1 else speed 292 | 293 | ix = inclination[0] * 14 294 | iy = inclination[1] * 14 295 | 296 | direction = math.cos(np.arctan2(y, x)) 297 | print 'direction: ', direction 298 | T = 2200 - 2000*speed 299 | 300 | x_u = 1 if x > 0 else -1 301 | y_u = 1 if y > 0 else -1 302 | 303 | init_ref = time.time() 304 | final_ref = init_ref + 25.0/1000 305 | 306 | for i in range(len(self.osc)): 307 | self.osc[i].ref_time = init_ref 308 | 309 | x_amp = 20 310 | z_amp = 15 311 | front_x = y * 15 312 | period = [T, T, T, T, T, T, T, T] 313 | amplitude = [x_amp, x_amp, z_amp, z_amp, x_amp, x_amp, z_amp, z_amp] 314 | 315 | offset = [] 316 | offset.append(front_x) 317 | offset.append(front_x) 318 | offset.append(-25+ix-iy) 319 | offset.append(25+ix+iy) 320 | offset.append(front_x) 321 | offset.append(front_x) 322 | offset.append(25-ix-iy) 323 | offset.append(-25-ix+iy) 324 | 325 | phase = [] 326 | phase.append(90*-y_u - 90*direction) 327 | phase.append(90*+y_u + 90*direction) 328 | phase.append(180 - 90*abs(direction)) 329 | phase.append(180 - 90*abs(direction)) 330 | phase.append(90*+y_u - 90*direction) 331 | phase.append(90*-y_u + 90*direction) 332 | phase.append(180 - 90*abs(direction)) 333 | phase.append(180 - 90*abs(direction)) 334 | 335 | while(final_ref > time.time()): 336 | for i in range(len(self.osc)): 337 | self.osc[i].period = period[i] 338 | self.osc[i].amplitude = amplitude[i] * speed 339 | self.osc[i].phase = phase[i] + global_phase 340 | self.osc[i].offset = offset[i] 341 | 342 | try: 343 | for i in range(len(self.osc)): 344 | self.osc[i].refresh() 345 | 346 | for i in range(len(self.osc)): 347 | self.controller.move(self._servo_pins[i], self.osc[i].output) 348 | 349 | 350 | except IOError: 351 | self._bus = smbus.SMBus(self._i2c_bus) 352 | 353 | present_phase = float(self.osc[5].delta_time)/self.osc[5].period * 360 354 | print 'Deltatime', self.osc[5].delta_time 355 | return present_phase 356 | --------------------------------------------------------------------------------