Skip to content

Commit

Permalink
Blockly and teleoperating interface (#63)
Browse files Browse the repository at this point in the history
* add v0 front interface

* start v1 front

* add btn interface on main + add choice between autonome/manual mode

* add value mon front bot

* add back manual controle

* add start blocky test

* add new block generator

* add first v of block

* change langue en cours

* correction langue

* new design prog part

* update blockly lib

* changement de langue blockly

* correction path

* correction import [a tester]

* correction front accueil + import + request

* add new verison int teleoperantion [ok]

* add define rosa for /exec

* add temp file for /execute

* correction block exec

* add speed gestion on blocks

* correction stdrrout

* impementation led + start sensor

* correction value aruco

* add generator code for ground + change langue

* block ground sensor ok

* replacement of arrow by joystick

* add block all sensor + refonte design + sensor value on int

* Patch Joystick + Direction de déplacement

* add sound block

* add new melodie + new logo
  • Loading branch information
Manon-Arc authored Jul 2, 2024
1 parent 40dd61c commit 13b82e6
Show file tree
Hide file tree
Showing 26 changed files with 5,983 additions and 2 deletions.
6 changes: 4 additions & 2 deletions api/python/examples/comportements/treasure/aruco_nest.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def follow_marker(rosa, target, marker_size, max_speed=0.3, stop_size=700):
turn = target_x

if marker_size < stop_size:
print(f"marker size {marker_size}")
if abs(target_x) > 0.4: # Seulement si le marqueur n'est pas assez centré
print("Non centré")
set_speed_aruco(rosa, turn, speed)
Expand Down Expand Up @@ -88,6 +89,7 @@ def go_to_aruco(rosa, img):
marker_size = calculate_marker_size(corners[0][0])
target = calculate_target_aruco(center, (1280, 960))
print(target)
change_state = follow_marker(rosa, target, marker_size, stop_size=350)
print("fin else go to aruco")
change_state = follow_marker(rosa, target, marker_size, stop_size=320)

return change_state
return change_state
88 changes: 88 additions & 0 deletions web/app/blueprints/interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import subprocess
import os
import sys
import threading
from rosa import Rosa
from flask import Blueprint, jsonify, request, send_from_directory

from ..controle.manuel import control, get_ground_value, get_distance_value

interface = Blueprint('interface', __name__)
dir_available = ['forward', 'backward', 'left', 'right', 'stop']

@interface.route('/manuel', methods=['GET'])
def activeManuel():
try:
dir = request.args.get("dir")
speed = request.args.get("speed", default=0.2, type=float)
if not dir or not speed:
return jsonify({'error': 'Missing parameters'}), 400
if dir in dir_available:
try:
control(dir, speed)
return jsonify({'message': 'Update successful'}), 200
except ValueError as e:
return jsonify({'error': str(e)}), 500
else:
return jsonify({'error': 'Invalid direction'}), 400
except Exception as e:
return jsonify({'error': str(e)}), 500

@interface.route('/sensor_values')
def sensor_values():
valueGL = get_ground_value("left")
valueGR = get_ground_value("right")
valueAv1 = get_distance_value(4)
valueAv2 = get_distance_value(3)
valueAv3 = get_distance_value(2)
valueAv4 = get_distance_value(1)
valueAv5 = get_distance_value(0)
valueAr1 = get_distance_value(5)
valueAr2 = get_distance_value(6)

return jsonify({"GL": str(valueGL),
"GR": str(valueGR),
"Av1": str(valueAv1),
"Av2": str(valueAv2),
"Av3": str(valueAv3),
"Av4": str(valueAv4),
"Av5": str(valueAv5),
"Ar1": str(valueAr1),
"Ar2": str(valueAr2)}
), 200


@interface.route('/execute', methods=['POST'])
def execute_code():
code = request.json.get('code', '')
try:
# Créez le fichier de contexte
context_code = """
from time import sleep
from app.controle.manuel import control, rosa, define_rosa, forward, backward, turn_left, turn_right, stop, active_led, get_ground_value, get_distance_value, play_note, play_notes, make_musique
{}
""".format(code)

# Écrivez le code de contexte dans un fichier temporaire
with open('context_code.py', 'w') as f:
f.write(context_code)

# Exécutez le fichier de contexte et capturez la sortie
process = subprocess.Popen(
['python3', 'context_code.py'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout, stderr = process.communicate()

# Supprimez le fichier temporaire après l'exécution
os.remove('context_code.py')

return jsonify({
'stdout': stdout.decode('utf-8'),
'stderr': stderr.decode('utf-8')
})
except Exception as e:
return jsonify({'error': str(e)}), 500
183 changes: 183 additions & 0 deletions web/app/controle/manuel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
from time import sleep
from rosa import Rosa

# Définition de la variable globale
rosa = None

def define_rosa():
global rosa
if rosa is None:
rosa = Rosa('rosa.local', local_robot=False) # Initialisez Rosa correctement
return rosa

def set_speed(ls, rs):
"""Réglez la vitesse des roues gauche et droite."""
global rosa
define_rosa()
speedls = max(min(ls, 1), -1) # Assure que la vitesse est entre 0 et 1
speedrs = max(min(rs, 1), -1) # Assure que la vitesse est entre 0 et 1

rosa.left_wheel.speed = speedls
rosa.right_wheel.speed = speedrs

def stop():
"""Arrêtez le ROSA."""
set_speed(0, 0)

def forward(speed, duration=None):
"""Fait avancer le ROSA à la vitesse donnée pendant une durée donnée ou indéfiniment."""
set_speed(speed, speed)
if duration:
sleep(duration)
stop()

def backward(speed, duration=None):
"""Fait reculer le ROSA à la vitesse donnée pendant une durée donnée ou indéfiniment."""
set_speed(-speed, -speed)
if duration:
sleep(duration)
stop()

def turn_left(speed, duration=None):
"""Fait tourner le ROSA à gauche à la vitesse donnée pendant une durée donnée ou indéfiniment."""
set_speed(-speed, speed)
if duration:
sleep(duration)
stop()

def turn_right(speed, duration=None):
"""Fait tourner le ROSA à droite à la vitesse donnée pendant une durée donnée ou indéfiniment."""
set_speed(speed, -speed)
if duration:
sleep(duration)
stop()

def active_led(led, color, duration=2):
"""Met la led indiquée à l'état indiqué"""
global rosa
define_rosa()
duration = max(duration, 2)

colors = {
"red": [32, 0, 0],
"green": [0, 32, 0],
"blue": [0, 0, 32],
"yellow": [32, 32, 0],
"purple": [32, 0, 32],
"cyan": [0, 32, 32],
"white": [32, 32, 32]
}

if color in colors:
color_value = colors[color]

if led == "left":
rosa.leds.bottom.left.color = color_value
elif led == "right":
rosa.leds.bottom.right.color = color_value
elif led == "both":
rosa.leds.bottom.right.color = color_value
rosa.leds.bottom.left.color = color_value
sleep(duration)

else:

print(f"Couleur {color} non reconnue. Utilisez une des couleurs suivantes : {', '.join(colors.keys())}.")


def get_ground_value(sensor):
"""renvoit la valeur du capteur de sol indiqué"""
global rosa
define_rosa()
reflected = rosa.ground_reflected

if sensor == "left":
return reflected[0]
elif sensor == "right":
return reflected[1]
else :
return (f"{sensor} non reconnue.")

def get_distance_value(sensor):
"""renvoit la valeur des capteurs avant"""
global rosa
define_rosa()
reflected = rosa.prox_horizontal[sensor]
return reflected

def play_note(hertz, ds):
"""Joue une seule note."""
global rosa
define_rosa()
ds *= 100
rosa.sound.frequency(hertz, ds)

def play_notes(notes):
"""Joue une série de notes."""
for hertz, ds in notes:
if hertz == 0 :
sleep(ds)
else :
play_note(hertz, ds)
sleep(ds)

def make_musique(song):
"""Joue une mélodie définie."""
global rosa
define_rosa()

melodies = {
"twinkle_twinkle": [
[392, 0.5], [392, 0.5], [587, 0.5], [587, 0.5], [659, 0.5], [659, 0.5], [587, 1],
[523, 0.5], [523, 0.5], [494, 0.5], [494, 0.5], [440, 0.5], [440, 0.5], [392, 1],
[587, 0.5], [587, 0.5], [523, 0.5], [523, 0.5], [494, 0.5], [494, 0.5], [440, 1],
[587, 0.5], [587, 0.5], [523, 0.5], [523, 0.5], [494, 0.5], [494, 0.5], [440, 1]
],
"frere_jacques": [
[261, 0.5], [294, 0.5], [330, 0.5], [261, 0.5],
[261, 0.5], [294, 0.5], [330, 0.5], [261, 0.5],
[330, 0.5], [349, 0.5], [392, 0.5],
[330, 0.5], [349, 0.5], [392, 0.5],
[392, 0.25], [440, 0.25], [392, 0.25], [349, 0.25], [330, 0.5], [261, 0.5],
[392, 0.25], [440, 0.25], [392, 0.25], [349, 0.25], [330, 0.5], [261, 0.5],
[261, 0.5], [392, 0.5], [261, 0.5]
],
"joyeux_anniversaire": [
[264, 0.25], [264, 0.25], [297, 0.5], [264, 0.5], [352, 0.5], [330, 1.0],
[264, 0.25], [264, 0.25], [297, 0.5], [264, 0.5], [396, 0.5], [352, 1.0],
[264, 0.25], [264, 0.25], [528, 0.5], [440, 0.5], [352, 0.5], [330, 0.5], [297, 1.0],
[466, 0.25], [466, 0.25], [440, 0.5], [352, 0.5], [396, 0.5], [352, 1.0]
],
"super_mario": [
[659, 0.03125], [659, 0.03125], [0, 0.03125], [659, 0.03125], [0, 0.03125], [523, 0.03125], [659, 0.03125], [784, 0.03125], [0, 0.09375], [392, 0.03125],
[0, 0.09375], [523, 0.03125], [0, 0.0625], [392, 0.03125], [0, 0.0625], [330, 0.03125], [0, 0.0625], [440, 0.03125], [0, 0.03125], [494, 0.03125],
[0, 0.03125], [466, 0.03125], [0, 0.0105], [440, 0.03125], [0, 0.03125], [392, 0.03125], [659, 0.03125], [784, 0.03125], [880, 0.03125], [0, 0.03125],
[698, 0.03125], [784, 0.03125], [0, 0.03125], [659, 0.03125], [0, 0.03125], [523, 0.03125], [587, 0.03125], [494, 0.03125], [0, 0.03125],
[523, 0.03125], [0, 0.0625], [392, 0.03125], [0, 0.0625], [330, 0.03125], [0, 0.0625], [440, 0.03125], [0, 0.03125], [494, 0.03125], [0, 0.03125],
[466, 0.03125], [0, 0.0105], [440, 0.03125], [0, 0.03125], [392, 0.03125], [659, 0.03125], [784, 0.03125], [880, 0.03125], [0, 0.03125],
[698, 0.03125], [784, 0.03125], [0, 0.03125], [659, 0.03125], [0, 0.03125], [523, 0.03125], [587, 0.03125], [494, 0.03125]
],
}

if song in melodies:
play_notes(melodies[song])
else:
print(f"Mélodie '{song}' non trouvée.")

def control(command, speed=None):
"""Contrôle le ROSA en fonction de la commande et de la vitesse donnée."""
global rosa
define_rosa()

if command == 'forward':
forward(speed)
elif command == 'backward':
backward(speed)
elif command == 'left':
turn_left(speed)
elif command == 'right':
turn_right(speed)
elif command == 'stop':
stop()
else:
raise ValueError(f"Commande inconnue : {command}")
8 changes: 8 additions & 0 deletions web/app/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
from .blueprints.api import api
from .blueprints.programs import programs
from .blueprints.webcam import webcam
from .blueprints.interface import interface

main = Blueprint("main", __name__)
main.register_blueprint(api, url_prefix='/api')
main.register_blueprint(programs, url_prefix='/program')
main.register_blueprint(webcam, url_prefix='/webcam')
main.register_blueprint(interface, url_prefix='/interface')

@main.context_processor
def inject_robot_config():
Expand Down Expand Up @@ -56,6 +58,7 @@ def program():

return render_template("modules/program.html", programs=program_data['programs'])


@main.route("/settings")
def settings():
return render_template("modules/settings.html")
Expand All @@ -79,3 +82,8 @@ def shutdown():
@main.route("/reboot")
def reboot():
return None


@main.route("/interface")
def interface():
return render_template("modules/interface.html")
Loading

0 comments on commit 13b82e6

Please sign in to comment.