├── 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 |
--------------------------------------------------------------------------------