feat: added dimensions

This commit is contained in:
Pierre Tellier 2025-03-31 16:18:39 +02:00
parent 6dc95dd176
commit 4f6efed9a3

453
control.py Normal file
View File

@ -0,0 +1,453 @@
import math
import numpy as np
def sandbox(t):
"""
python simulator.py -m sandbox
Un premier bac à sable pour faire des expériences
La fonction reçoit le temps écoulé depuis le début (t) et retourne une position cible
pour les angles des 12 moteurs
- Entrée: t, le temps (secondes écoulées depuis le début)
- Sortie: un tableau contenant les 12 positions angulaires cibles (radian) pour les moteurs
"""
# Par exemple, on envoie un mouvement sinusoidal
targets = [0] * 12
targets[0] = t ** 3
targets[1] = 0
targets[2] = 0
targets[3] = t ** 3
targets[4] = 0
targets[5] = 0
targets[6] = t ** 3
targets[7] = 0
targets[8] = 0
targets[9] = t ** 3
targets[10] = 0
targets[11] = 0
return targets
def direct(alpha, beta, gamma):
"""
python simulator.py -m direct
Le robot est figé en l'air, on ne contrôle qu'une patte
Reçoit en argument la cible (alpha, beta, gamma) des degrés de liberté de la patte, et produit
la position (x, y, z) atteinte par le bout de la patte
- Sliders: les angles des trois moteurs (alpha, beta, gamma)
- Entrées: alpha, beta, gamma, la cible (radians) des moteurs
- Sortie: un tableau contenant la position atteinte par le bout de la patte (en mètres)
"""
l1, l2, l3 = 45, 65, 87
"""Prend en entrée les angles moteurs et produit la position atteinte"""
xp = l1 + math.cos(beta) * l2 + math.cos(beta + gamma) * l3
yp = math.sin(beta) * l2 + math.sin(beta + gamma) * l3
x = math.cos(alpha) * xp
y = math.sin(alpha) * xp
z = yp
return x, y, z
from math import *
l1h = 0.049
l1v = 0.032
l1 = sqrt(l1h ** 2 + l1v ** 2)
l2h = 0.0605
l2v = 0.02215
l2 = sqrt(l2h ** 2 + l2v ** 2)
l3h = 0.013
l3v = 0.093
l3 = sqrt(l3h ** 2 + l3v ** 2)
tete_x = 0.095
patte_x = 0.032
patte_y = 0.079
def inverse(x, y, z):
"""
python simulator.py -m inverse
Le robot est figé en l'air, on ne contrôle qu'une patte
Reçoit en argument une position cible (x, y, z) pour le bout de la patte, et produit les angles
(alpha, beta, gamma) pour que la patte atteigne cet objectif
- Sliders: la position cible x, y, z du bout de la patte
- Entrée: x, y, z, une position cible dans le repère de la patte (mètres), provenant du slider
- Sortie: un tableau contenant les 3 positions angulaires cibles (en radians)
"""
# Dimensions (m)
theta0 = atan2(y, x)
l = sqrt((x - l1h*cos(theta0)) ** 2 + (y - l1h*sin(theta0)) ** 2 + (z + l1v) ** 2)
param2 = -1 * (-(l ** 2) + l2 ** 2 + l3 ** 2) / (2 * l2 * l3)
if param2 > 1 or param2 < -1:
print("\033[94m" + f"Tentative d'acces a une position impossible (param2) ({x}, {y}, {z})" + "\033[0m")
param2 = 1 if param2 > 1 else -1
theta2 = acos(param2)
param1 = (-l3 ** 2 + l2 ** 2 + l ** 2) / (2 * l2 * l)
if param1 > 1 or param1 < -1:
print("\033[94m" + f"Tentative d'acces a une position impossible (param1) ({x}, {y}, {z})" + "\033[0m")
param1 = 1 if param1 > 1 else -1
theta1 = acos(param1) + asin(z / l)
return [-theta0, theta1, theta2]
def draw(t):
"""
python simulator.py -m draw
Le robot est figé en l'air, on ne contrôle qu'une patte
Le but est, à partir du temps donné, de suivre une trajectoire de triangle. Pour ce faire, on
utilisera une interpolation linéaire entre trois points, et la fonction inverse précédente.
- Entrée: t, le temps (secondes écoulées depuis le début)
- Sortie: un tableau contenant les 3 positions angulaires cibles (en radians)
"""
def interpol(x2, y2, z2, x1, y1, z1, t):
return t * x1 + (1 - t) * x2, t * y1 + (1 - t) * y2, t * z1 + (1 - t) * z2
p1 = [0.15, 0, 0]
p2 = [0.1, 0.1, 0]
p3 = [0.1, 0, 0.05]
positions = [p1, p2, p3]
ratio = (t % 1)
partie = floor((t % 3))
partie2 = (partie + 1) % 3
# print(f"partie: {partie}, partie2: {partie2}")
wanted_origin_x, wanted_origin_y, wanted_origin_z = positions[partie][0], positions[partie][1], positions[partie][
2],
wanted_dest_x, wanted_dest_y, wanted_dest_z = positions[partie2][0], positions[partie2][1], positions[partie2][2],
# print(wanted_origin_x, wanted_origin_y, wanted_origin_z)
# print(wanted_dest_x, wanted_dest_y, wanted_dest_z)
# print(t)
wanted_x, wanted_y, wanted_z = interpol(wanted_origin_x, wanted_origin_y, wanted_origin_z, wanted_dest_x,
wanted_dest_y, wanted_dest_z, ratio)
return inverse(wanted_x, wanted_y, wanted_z)
def legs(targets_robot):
"""
python simulator.py -m legs
Le robot est figé en l'air, on contrôle toute les pattes
- Sliders: les 12 coordonnées (x, y, z) du bout des 4 pattes
- Entrée: des positions cibles (tuples (x, y, z)) pour le bout des 4 pattes
- Sortie: un tableau contenant les 12 positions angulaires cibles (radian) pour les moteurs
"""
targets = [0] * 18
cos_val = [1, 1, 0, -1, -1, 0]
sin_val = [0, 0, 1, 0, 0, -1]
offset_x = [patte_x, patte_x, tete_x, patte_x, patte_x, tete_x]
offset_y = [patte_y, patte_y, 0, patte_y, patte_y, 0]
for i in range(6):
target_x, target_y, target_z = targets_robot[i]
target_x = cos_val[i] * target_x + sin_val[i] * target_x
target_y = cos_val[i] * target_y + sin_val[i] * target_y
target_x += patte_x * offset_x[i]
target_y += patte_y * offset_y[i]
alpha, beta, gamma = inverse(target_x, target_y, target_z)
targets[3 * i] = alpha
targets[3 * i + 1] = beta
targets[3 * i + 2] = gamma
return targets
def interpol2(point2, point1, t):
x1, y1, z1 = point1
x2, y2, z2 = point2
return t * x1 + (1 - t) * x2, t * y1 + (1 - t) * y2, t * z1 + (1 - t) * z2
def walkV1(t, speed_x, speed_y, speed_rotation):
"""
python simulator.py -m walk
Le but est d'intégrer tout ce que nous avons vu ici pour faire marcher le robot
- Sliders: speed_x, speed_y, speed_rotation, la vitesse cible du robot
- Entrée: t, le temps (secondes écoulées depuis le début)
speed_x, speed_y, et speed_rotation, vitesses cibles contrôlées par les sliders
- Sortie: un tableau contenant les 12 positions angulaires cibles (radian) pour les moteurs
"""
# t = t*speed_x * 20
num_patte = 4
slider_max = 0.200
neutral_position = np.array([
[-0.06, 0.06, -0.13],
[-0.06, -0.06, -0.13],
[0.06, -0.06, -0.13], # [0.15, 0.15, -0.01],
[0.06, 0.06, -0.13]
])
real_position = np.copy(neutral_position)
movement_x = np.array([
[0.0, 0, 0],
[0.06, 0, 0],
[-0.06, 0, 0],
])
movement_y = np.array([
[0.0, 0, 0],
[0, 0.06, 0],
[0, -0.06, 0],
])
movement_z = np.array([
[0, 0, 0.02],
[0, 0, -0.01],
[0, 0, -0.01]
])
step_duration = np.array([0.05, 0.3, 0.05])
step_count = len(movement_z)
movement_duration = np.sum(step_duration)
assert len(
step_duration) == step_count, f"all movements steps must have a length, currently, {len(step_duration)}/{step_count} have them"
def get_current_step(t):
time_passed = 0
for i in range(len(step_duration)):
time_passed += step_duration[i]
if t % movement_duration < time_passed:
return i
def get_current_step_advancement(t):
current_step = get_current_step(t)
t = t % movement_duration
for i in range(0, current_step):
t -= step_duration[i]
return t / step_duration[current_step]
def get_next_step(t):
return floor((get_current_step(t) + 1) % step_count)
def rotate(patte):
return [1, -1, -1, 1][
patte] * movement_x # + [-1, 1, -1, 1][patte] * movement_y # mettre des 0 partout sur le Y fait une très belle rotation
def normalize(matrix, slider_max, speed):
return (matrix / slider_max) * speed
offsets = np.array([0, 0.5, 0, 0.5]) * movement_duration # offset between each leg
assert len(offsets) == num_patte, f"all offsets must be set, currently, {len(offsets)}/{num_patte} have them"
for patte in range(num_patte):
time = t + offsets[patte]
mov_index_start = get_current_step(time)
mov_index_end = get_next_step(time)
mov_start_x = normalize(movement_x[mov_index_start], slider_max, speed_x)
mov_end_x = normalize(movement_x[mov_index_end], slider_max, speed_x)
mov_start_y = normalize(movement_y[mov_index_start], slider_max, speed_y)
mov_end_y = normalize(movement_y[mov_index_end], slider_max, speed_y)
mov_start_z = movement_z[mov_index_start]
mov_end_z = movement_z[mov_index_end]
mov_start_rotate = normalize(rotate(patte)[mov_index_start], 0.5, speed_rotation)
mov_end_rotate = normalize(rotate(patte)[mov_index_end], 0.5, speed_rotation)
mov_start = neutral_position[patte] + mov_start_z + mov_start_x + mov_start_y + mov_start_rotate
mov_end = neutral_position[patte] + mov_end_z + mov_end_x + mov_end_y + mov_end_rotate
(real_position[patte][0],
real_position[patte][1],
real_position[patte][2]) = interpol2(mov_start, mov_end, get_current_step_advancement(time))
print(
f"[{patte}] [{get_current_step(time)}->{get_next_step(time)}], start: {mov_start}, end: {mov_end}, current ({real_position[patte][0]}, {real_position[patte][1]}, {real_position[patte][2]})")
return legs(real_position)
def translate(tx, ty, tz):
return np.array([
[1.0, 0.0, 0.0, tx],
[0.0, 1.0, 0.0, ty],
[0.0, 0.0, 1.0, tz],
[0.0, 0.0, 0.0, 1.0],
])
def Rx(alpha):
return np.array([
[1.0, 0.0, 0.0, 0.0],
[0.0, np.cos(alpha), -np.sin(alpha), 0.0],
[0.0, np.sin(alpha), np.cos(alpha), 0.0],
[0.0, 0.0, 0.0, 1.0],
])
def Ry(alpha):
return np.array([
[np.cos(alpha), 0.0, -np.sin(alpha), 0.0],
[0.0, 1.0, 0.0, 0.0],
[np.sin(alpha), 0.0, np.cos(alpha), 0.0],
[0.0, 0.0, 0.0, 1.0],
])
def Rz(alpha):
return np.array([
[np.cos(alpha), -np.sin(alpha), 0.0, 0.0],
[np.sin(alpha), np.cos(alpha), 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
])
# multiplication de matrices: @
# gauche: monde, droite: repere
def walkV2(t, speed_x, speed_y, speed_rotation):
def get_rotation_center(speed_x, speed_y, theta_point):
direction = np.array([-speed_y, speed_x])
return direction / max(0.001, theta_point)
x0, y0 = get_rotation_center(speed_x, speed_y, speed_rotation)
print(x0, y0)
"""
python simulator.py -m walk
Le but est d'intégrer tout ce que nous avons vu ici pour faire marcher le robot
- Sliders: speed_x, speed_y, speed_rotation, la vitesse cible du robot
- Entrée: t, le temps (secondes écoulées depuis le début)
speed_x, speed_y, et speed_rotation, vitesses cibles contrôlées par les sliders
- Sortie: un tableau contenant les 12 positions angulaires cibles (radian) pour les moteurs
"""
# t = t*speed_x * 20
num_patte = 4
slider_max = 0.200
neutral_position = np.array([
[-0.06, 0.06, -0.13],
[-0.06, -0.06, -0.13],
[0.06, -0.06, -0.13], # [0.15, 0.15, -0.01],
[0.06, 0.06, -0.13]
])
real_position = np.copy(neutral_position)
movement_z = np.array([
[0, 0, 0.02],
[0, 0, -0.01],
[0, 0, -0.01]
])
step_duration = np.array([0.05, 0.3, 0.05])
step_count = len(movement_z)
movement_duration = np.sum(step_duration)
assert len(
step_duration) == step_count, f"all movements steps must have a length, currently, {len(step_duration)}/{step_count} have them"
def get_current_step(t):
time_passed = 0
for i in range(len(step_duration)):
time_passed += step_duration[i]
if t % movement_duration < time_passed:
return i
def get_current_step_advancement(t):
current_step = get_current_step(t)
t = t % movement_duration
for i in range(0, current_step):
t -= step_duration[i]
return t / step_duration[current_step]
def get_next_step(t):
return floor((get_current_step(t) + 1) % step_count)
offsets = np.array([0, 0.5, 0, 0.5]) * movement_duration # offset between each leg
assert len(offsets) == num_patte, f"all offsets must be set, currently, {len(offsets)}/{num_patte} have them"
for patte in range(num_patte):
time = t + offsets[patte]
mov_index_start = get_current_step(time)
mov_index_end = get_next_step(time)
mov_start_z = movement_z[mov_index_start]
mov_end_z = movement_z[mov_index_end]
mov_start = neutral_position[patte] + mov_start_z
mov_end = neutral_position[patte] + mov_end_z
(real_position[patte][0],
real_position[patte][1],
real_position[patte][2]) = interpol2(mov_start, mov_end, get_current_step_advancement(time))
dthteta = speed_rotation * 2
theta = dthteta * (get_current_step_advancement(time) - 0.5)
# theta = 0
print(theta)
# rotating the vector
x1, y1 = real_position[patte][0], real_position[patte][1]
print(f"x1: {x1}, y1: {y1}")
x1t, y1t = x1 - x0, y1 - y0
x2t, y2t = x1t * cos(theta) + y1t * sin(theta), x1t * sin(theta) + y1t * cos(theta)
x2, y2 = x2t + x0, y2t + y0
print(f"x2: {x2}, y2: {y2}")
if mov_index_start == 1:
# theta += time
# xp = d * cos(theta) + real_position[patte][0]
real_position[patte][0] = x2
real_position[patte][1] = y2
# print(
# f"[{patte}] [{get_current_step(time)}->{get_next_step(time)}], start: {mov_start}, end: {mov_end}, current ({real_position[patte][0]}, {real_position[patte][1]}, {real_position[patte][2]})")
return legs(real_position)
walk = walkV1
if __name__ == "__main__":
print("N'exécutez pas ce fichier, mais simulator.py")