diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e27b75ee2ea5b54da241dc66e908a3caeda6c41..a9d301620bac4f0ef990d2bf1e2be16aa70ead62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,6 +163,8 @@ catkin_install_python(PROGRAMS scripts/play_animation.py scripts/reset_display.py scripts/build_still_ensemble.py + scripts/build_state_cycle.py + scripts/write.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} ) diff --git a/scripts/build_state_cycle.py b/scripts/build_state_cycle.py new file mode 100755 index 0000000000000000000000000000000000000000..c932bf06f528ca2a816b4494ec77b89f572c4903 --- /dev/null +++ b/scripts/build_state_cycle.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +import argparse + +from visualising.expression.tool import Tool +from visualising.communication.arduino import Arduino +from visualising.communication.channel.connection import Connection + +parser = argparse.ArgumentParser(description="script to play an animation") +parser.add_argument("-p", "--port", help="port to which the Arduino is connected", type=str, default="/dev/ttyUSB0") +parser.add_argument("-b", "--baud", help="baud rate of the connection", type=int, default=57600) +parser.add_argument("-t", "--time", help="time between ensembles", type=int, default=100) +args = vars(parser.parse_args()) + +port = args["port"] +baud = args["baud"] +time = args["time"] + +color = [10, 10, 10] + +arduino = Arduino(Connection(port, baud)) + +states = [0.4, 1, 0.2, 0.3, 0.9, 0.7] +ensemble = Tool.build_state_cycle(states) + +arduino.stream_animation(Tool.highlight(ensemble, states, 4)) diff --git a/scripts/build_still_ensemble.py b/scripts/build_still_ensemble.py old mode 100644 new mode 100755 diff --git a/scripts/reset_display.py b/scripts/reset_display.py old mode 100644 new mode 100755 diff --git a/scripts/write.py b/scripts/write.py new file mode 100755 index 0000000000000000000000000000000000000000..ec35159da0f23c773e25d38c7a6f20fbdebb5949 --- /dev/null +++ b/scripts/write.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +import argparse + +from visualising.expression.tool import Tool +from visualising.communication.arduino import Arduino +from visualising.communication.illustration.animation import Animation +from visualising.communication.channel.connection import Connection + +parser = argparse.ArgumentParser(description="script to play an animation") +parser.add_argument("-p", "--port", help="port to which the Arduino is connected", type=str, default="/dev/ttyUSB0") +parser.add_argument("-b", "--baud", help="baud rate of the connection", type=int, default=57600) +parser.add_argument("-t", "--time", help="time between ensembles", type=int, default=100) +args = vars(parser.parse_args()) + +port = args["port"] +baud = args["baud"] +time = args["time"] + +arduino = Arduino(Connection(port, baud)) + +animation = Tool.write("HALLO") + +arduino.stream_animation(Animation(animation, time, 1)) diff --git a/src/visualising/communication/arduino.py b/src/visualising/communication/arduino.py index f07fa4e5611fcb9a758a38eb72a7a83a5f83b2e4..0bfaf9b740cb240987eb419f949d779d09fc4fb5 100755 --- a/src/visualising/communication/arduino.py +++ b/src/visualising/communication/arduino.py @@ -64,11 +64,16 @@ class Arduino: if time.time() > curr_time + playback_time * 10: raise ArduinoException("Successful playback of animation can't be confirmed!") - # Streams an entities to the Arduino. For this purpose, two frames_library of - # the entities are repeatedly sent to the Arduino. The Arduino plays - # the received frames_library and then confirms the playback, whereupon new - # frames_library are sent. + # Streams an animation to the Arduino. For this purpose, one ensemble of + # the animation is repeatedly sent to the Arduino. The Arduino plays + # the received ensemble and then confirms the playback, whereupon a new + # ensemble is sent. def stream_animation(self, animation): for _ in range(animation.num_iter): for ensemble in animation.ensembles: self.play_animation(Animation([ensemble], animation.ensemble_time, 1)) + + # Streams consecutively animations to the Arduino. + def stream_multiple_animations(self, animation_list): + for animation in animation_list: + self.stream_animation(animation) diff --git a/src/visualising/expression/artificial.py b/src/visualising/expression/artificial.py new file mode 100644 index 0000000000000000000000000000000000000000..bc73772c414a408a47190ed3ab7b9554219e3230 --- /dev/null +++ b/src/visualising/expression/artificial.py @@ -0,0 +1,14 @@ +import time +import rospy +import numpy as np + +from visualising.expression.tool import Tool + +class State: + def __init__(self, arduino): + self.arduino = arduino + self.playback_start = 0 + + def react(self, state): + if len(state[1]) > 16: + rospy.logwarn("Only displaying the first 16 metric aggregations!") diff --git a/src/visualising/expression/expression.py b/src/visualising/expression/emotion.py old mode 100755 new mode 100644 similarity index 74% rename from src/visualising/expression/expression.py rename to src/visualising/expression/emotion.py index 32340356e0ab85eb40f453ac37a6052f4bad11aa..bb37410107b12c15cbd60cc0df968bf6ebe41c1e --- a/src/visualising/expression/expression.py +++ b/src/visualising/expression/emotion.py @@ -1,5 +1,4 @@ import time -import numpy as np from visualising.expression.tool import Tool @@ -9,17 +8,10 @@ class Emotion: self.arduino = arduino self.playback_start = 0 - @staticmethod - def state_to_color(state): - red = np.array([25, 0, 0]) - white = np.array([25, 25, 25]) - - return (1 - state) * white + state * red - def react(self, state): if time().time - playback_start > 5: - color = Emotion.state_to_color(state[0]) + color = Tool.state_to_color(state[0], [25, 25, 25], [25, 0, 0]) if state[0] > 0.7: self.happy(color) @@ -43,13 +35,3 @@ class Emotion: ensembles = Tool.create_ensembles("expression_angry.txt") self.arduino.stream_animation(Tool.build_still_ensemble_parallel(ensembles[0], 50, color)) self.playback_start = time.time() - - -class State: - def __init__(self, arduino): - self.arduino = arduino - self.playback_start = 0 - - def react(self, state): - pass - diff --git a/src/visualising/expression/library/A.txt b/src/visualising/expression/library/A.txt new file mode 100644 index 0000000000000000000000000000000000000000..23b7df9b0aa82254ef9c3e67dfaa292a236bed7e --- /dev/null +++ b/src/visualising/expression/library/A.txt @@ -0,0 +1,2 @@ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +10,10,10,10,10,10,10,10,10,10,10,10,10,0,0,10,10,10,10,10,10,0,0,0,0,0,0,0,0,0,10,10,10,10,10,10,10,0,0,10,10,10,10,10,10,10,10,10 diff --git a/src/visualising/expression/library/B.txt b/src/visualising/expression/library/B.txt new file mode 100644 index 0000000000000000000000000000000000000000..e0f20c8d962f766e9331a23df6496dd615a5a1e3 --- /dev/null +++ b/src/visualising/expression/library/B.txt @@ -0,0 +1,2 @@ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,10,10,10,10,10,10,10,10,10,10,0,0,10,10,10,10,10,10,10,10,10 \ No newline at end of file diff --git a/src/visualising/expression/library/C.txt b/src/visualising/expression/library/C.txt new file mode 100644 index 0000000000000000000000000000000000000000..ee479a4abaffe885abd64b97b99ff0c131babc21 --- /dev/null +++ b/src/visualising/expression/library/C.txt @@ -0,0 +1,2 @@ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,0,0,0,0,0,0,10,10,10,10,10,10,10,10,10 \ No newline at end of file diff --git a/src/visualising/expression/library/D.txt b/src/visualising/expression/library/D.txt new file mode 100644 index 0000000000000000000000000000000000000000..30179eda766ee2c50250c6d3dbdc83467661c382 --- /dev/null +++ b/src/visualising/expression/library/D.txt @@ -0,0 +1,2 @@ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +10,10,10,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 \ No newline at end of file diff --git a/src/visualising/expression/library/E.txt b/src/visualising/expression/library/E.txt new file mode 100644 index 0000000000000000000000000000000000000000..11152ec90b62278971eaa095ba331cd0acb9ca80 --- /dev/null +++ b/src/visualising/expression/library/E.txt @@ -0,0 +1,2 @@ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +10,0,0,10,0,0,10,0,0,10,0,0,10,0,0,10,0,0,10,0,0,10,0,0,10,0,0,10,0,0,0,0,0,0,0,0,10,10,10,10,0,0,10,0,0,10,0,0 \ No newline at end of file diff --git a/src/visualising/expression/library/G.txt b/src/visualising/expression/library/G.txt new file mode 100644 index 0000000000000000000000000000000000000000..8023585baba26740880d98f9ee80aece9d4f38ea --- /dev/null +++ b/src/visualising/expression/library/G.txt @@ -0,0 +1,2 @@ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,0,0,0,0,0,0,0,0,10,10,10,10,10,10 \ No newline at end of file diff --git a/src/visualising/expression/library/H.txt b/src/visualising/expression/library/H.txt new file mode 100644 index 0000000000000000000000000000000000000000..7fc24e6ce25e41cce75994cd6ff117d85abdccfd --- /dev/null +++ b/src/visualising/expression/library/H.txt @@ -0,0 +1,2 @@ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,10,10,10,10,10,10,10,0,0,10,10,10,10,10,10,0,0,0,0,0,0,0,0,0,10,10,10,10,10,10,10,0,0,10,10,10,10,10,10,0,0,0 \ No newline at end of file diff --git a/src/visualising/expression/library/I.txt b/src/visualising/expression/library/I.txt new file mode 100644 index 0000000000000000000000000000000000000000..42bf3031dd3231d65f1ad2063ae38dfa0cbb9066 --- /dev/null +++ b/src/visualising/expression/library/I.txt @@ -0,0 +1,2 @@ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +10,0,0,10,10,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,10,10,0,0,10,10,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,10 \ No newline at end of file diff --git a/src/visualising/expression/library/J.txt b/src/visualising/expression/library/J.txt new file mode 100644 index 0000000000000000000000000000000000000000..dc32ec0a02f89cb90739dec648e01c7240d93e1c --- /dev/null +++ b/src/visualising/expression/library/J.txt @@ -0,0 +1,2 @@ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +1,1,1,1,1,1,10,10,10,0,0,0,0,0,0,0,0,0,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,1,1,1 \ No newline at end of file diff --git a/src/visualising/expression/library/L.txt b/src/visualising/expression/library/L.txt new file mode 100644 index 0000000000000000000000000000000000000000..3046ae8ac4a840ef128e3723c233032c00f9e32b --- /dev/null +++ b/src/visualising/expression/library/L.txt @@ -0,0 +1,2 @@ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,10,10,10,10,10,10,10,10,10,0,0,0,0,0,0,0,0,0,0,0,0 \ No newline at end of file diff --git a/src/visualising/expression/library/O.txt b/src/visualising/expression/library/O.txt new file mode 100644 index 0000000000000000000000000000000000000000..29d6d8de73b442ed8739df2a827f81a236b62a26 --- /dev/null +++ b/src/visualising/expression/library/O.txt @@ -0,0 +1,2 @@ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 \ No newline at end of file diff --git a/src/visualising/expression/tool.py b/src/visualising/expression/tool.py index 8d289231bbd8c460149e0667852025fcbd9cd09e..b635235e46d8deae7b9d3eb5fff1d7e6c340fcf9 100644 --- a/src/visualising/expression/tool.py +++ b/src/visualising/expression/tool.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import re +import numpy as np import importlib.resources from visualising.communication.illustration.animation import Animation @@ -81,6 +82,36 @@ class Tool: ensembles = Tool.create_ensembles(filename) return Animation(ensembles, ensemble_time, iterations) + @staticmethod + def create_empty_ensemble(): + l_pixels = [] + r_pixels = [] + for _ in range(16): + l_pixels.append(Pixel(0, 0, 0)) + r_pixels.append(Pixel(0, 0, 0)) + + return Ensemble(Frame(l_pixels), Frame(r_pixels)) + + @staticmethod + def set_pixel_color(ensemble, pixel, r, g, b): + if not 0 <= pixel <= 31: + raise ValueError("Pixel index must be an integer between 0 and 31!") + + if pixel < 16: + ensemble.l_frame.adjust_color(pixel, r, g, b) + else: + pixel = pixel - 16 + ensemble.r_frame.adjust_color(pixel, r, g, b) + + return ensemble + + @staticmethod + def state_to_color(state, start_color, end_color): + if not 0 <= state <= 1: + raise ValueError("State must be a value between 0 and 1!") + + return (1 - state) * np.array(start_color) + state * np.array(end_color) + @staticmethod def build_still_ensemble(still_ensemble, ensemble_time, color): r = color[0] @@ -150,40 +181,57 @@ class Tool: return Animation(ensembles, ensemble_time, 1) @staticmethod - def merge_frames(l_frames, r_frames): - if len(l_frames) != len(r_frames): - raise ValueError("Merging different number of frames_library not allowed!") - - merged_frames = [] - for l_frame, r_frame in zip(l_frames, r_frames): - merged_frames = merged_frames + l_frame - merged_frames = merged_frames + r_frame + def build_state_cycle(state_vector): + ensemble = Tool.create_empty_ensemble() + pixel = 0 + for state in state_vector: + color = Tool.state_to_color(state, [0, 50, 0], [50, 0, 0]) + ensemble = Tool.set_pixel_color(ensemble, pixel, color[0], color[1], color[2]) + pixel = pixel + 1 - return merged_frames + return ensemble @staticmethod - def concatenate_frames(start, end): - return start.extend(end) + def highlight(ensemble, states, number): + number = min(len(states), number) + highlight = sorted(zip(states, range(len(states))), reverse=True)[:number] - @staticmethod - def create_empty_ensemble(): - l_pixels = [] - r_pixels = [] - for _ in range(16): - l_pixels.append(Pixel(0, 0, 0)) - r_pixels.append(Pixel(0, 0, 0)) + ensembles = [ensemble] + for tuple in highlight: + empty = Tool.create_empty_ensemble() + for i in range(16): + if ensemble.l_frame.is_colored(i): - return Ensemble(Frame(l_pixels), Frame(r_pixels)) + r = ensemble.l_frame.pixels[i].r + g = ensemble.l_frame.pixels[i].g + b = ensemble.l_frame.pixels[i].b + + off = [0, 0, 0] + + if tuple[1] != i: + empty = Tool.set_pixel_color(empty, i, off[0], off[1], off[2]) + else: + empty = Tool.set_pixel_color(empty, i, r, g, b) + + ensembles.append(empty) + ensembles = ensembles + Tool.write("HALLO") + ensembles.append(ensemble) + + ensembles.append(Tool.create_empty_ensemble()) + + return Animation(ensembles, 500, 1) @staticmethod - def set_pixel_color(ensemble, pixel, r, g, b): - if not 0 <= pixel <= 31: - raise ValueError("Pixel index must be an integer between 0 and 31!") + def write(text): + if not text.isalpha(): + raise ValueError("Text must contain only alphabetic characters!") - if pixel < 16: - ensemble.l_frame.adjust_color(pixel, r, g, b) - else: - pixel = pixel - 16 - ensemble.r_frame.adjust_color(pixel, r, g, b) + text = text.upper() - return ensemble + ensembles = [] + for char in text: + file = char + ".txt" + ensembles = ensembles + Tool.create_ensembles(file) + ensembles.append(Tool.create_empty_ensemble()) + + return ensembles