Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

PiBot: Arduino-gesteuerter Klavierroboter für präzise Musikwiedergabe

Komponenten und Materialien

PiBot: Arduino-gesteuerter Klavierroboter für präzise Musikwiedergabe Arduino 101 × 1
Texas Instruments 74HC595 × 11
IRFZ44N (N-Kanal MOSFET) × 88
12V Batterie × 2
PiBot: Arduino-gesteuerter Klavierroboter für präzise Musikwiedergabe Kondensator 100 µF × 11
PiBot: Arduino-gesteuerter Klavierroboter für präzise Musikwiedergabe OpenBuilds Wire Cable – per Foot × 1
PiBot: Arduino-gesteuerter Klavierroboter für präzise Musikwiedergabe Breadboard (generic) oder Lochrasterplatine × 11
8-Bein IC-Sockel (optional, falls Sie keine teuren 595 verbrennen wollen) × 11
PiBot: Arduino-gesteuerter Klavierroboter für präzise Musikwiedergabe Widerstand 10 kΩ (nicht im Projekt verwendet, aber empfohlen für jeden MOSFET) × 88
PiBot: Arduino-gesteuerter Klavierroboter für präzise Musikwiedergabe 1N4007 – Hochspannungsdiode (nicht verwendet, aber empfohlen für jeden MOSFET) × 88

Benötigte Werkzeuge und Maschinen

PiBot: Arduino-gesteuerter Klavierroboter für präzise Musikwiedergabe Heißklebepistole

Apps und Online-Dienste

Processing
Python
Binary MIDI file to text (Konverter)

Über dieses Projekt

Wie alles begann

Vor vielen Jahren stellte Yamaha ein selbstspielendes Klavier vor. Als junger, unbescholtener Beobachter sah ich es hinter dem Schaufenster eines Musikgeschäfts spielen.

Kurz gesagt: Es gab keinen großen Grund für dieses Projekt – ich wollte es einfach bauen.

Übersicht

Ein einzelnes Arduino Mega Board kostet ca. 40 $, und man bräuchte zwei, um 88 Solenoide zu steuern – recht teuer. Stattdessen nimmt man einen günstigen Arduino Uno und 11 Schieberegister (Shift Registers). Ein Schieberegister ermöglicht die Steuerung vieler Ausgänge (üblicherweise LEDs) mit wenigen Pins. So steuert ein Arduino mit 11 Schieberegistern 88 Solenoide.

Da Schieberegister verwendet werden, sendet der PC eine Bitfolge an den Arduino statt MIDI-Befehle. Die MIDI-Datei wird vorher in eine Bitfolge umgewandelt.

Hardware

Die Solenoide aus China erwiesen sich als zu schwach, um Klaviertasten zu drücken. Das Drücken am innersten Punkt erfordert mehr Kraft, aber ich fand dies die schonendste Methode für das Klavier. Schließlich speiste ich 24 V in die 12 V-Solenoide, um genügend Kraft zu erzielen.

PiBot: Arduino-gesteuerter Klavierroboter für präzise Musikwiedergabe

88 Solenoide verbrauchen viel Strom. Da ich kein teures Netzteil kaufen konnte, nutzte ich die Autobatterie meines Vaters (der nun nicht mehr wegfahren kann!).

PiBot: Arduino-gesteuerter Klavierroboter für präzise Musikwiedergabe

Jedes Schieberegister und jeder MOSFET kommt auf eine eigene Steuerplatine.

PiBot: Arduino-gesteuerter Klavierroboter für präzise Musikwiedergabe

Der 74HC595 rechts ist mit einem Sockel bestückt, falls er durchbrennt (passierte einmal). Der Schaltplan entspricht exakt Beispiel 2 aus dem Datenblatt – ersetzen Sie die LEDs durch MOSFET-Gates. Auf Pull-Down-Widerstände wurde verzichtet, um Kosten und Lötaufwand zu sparen. Die MOSFETs schalten bei 5 V ein und unter 4 V aus – in zahllosen Tests bestätigt (theoretisch nicht ganz korrekt, aber praktisch funktionsfähig).

Zuletzt wird eine Kunststoffplatte verwendet, um die Solenoide aufzukleben. Heißkleber und Kunststoff sind wegen der Hitzeentwicklung nicht ideal, aber die günstigste Lösung.

Eine Seite der Solenoid-Kabel wird an den Pluspol der Batterie angeschlossen.

PiBot: Arduino-gesteuerter Klavierroboter für präzise Musikwiedergabe

Software

  1. Eine MIDI-Datei besorgen.
  2. Die MIDI-Datei in Textform umwandeln, z. B. über flashmusicgames.com/midi/mid2txt.php.
  3. Zeitangaben, Taktart und Tempo ignorieren. Das Tempo kann später mit dem Zeitwert multipliziert werden. Ziel ist eine Datei im Format: Zeit, On/Off, Kanal, Note, Velocity.
  4. Mit dem beiliegenden Python-Skript werden daraus 11 Blöcke à 8 Bit mit Zeitstempel erzeugt.
  5. Diese Daten werden über Processing (COM-Port) an den Arduino gesendet.

Siehe Anhänge, wie Processing die Daten sendet und der Arduino sie verarbeitet. Hinweis: Mein Programmierstil ist gewöhnungsbedürftig; Processing sendet die Daten von rechts nach links, weil der Arduino die Bits physikalisch von rechts nach links auf dem Klavier ausgibt.

Fehlersuche

Ein professioneller Ingenieur würde viele Probleme sehen – und sie existieren tatsächlich.

Da die Solenoide mit Heißkleber befestigt waren, führte Überhitzung zum Schmelzen des Klebers. Lösung: Entfernen und doppelseitiges Hitzeband (bis 150 °C) verwenden.

Code

Python-Code zur Übersetzung

output_file = open("translated.txt", "w")
input_file = open("megalocania.txt")
raw_input_data = input_file.read()
work_data = raw_input_data.splitlines()
result = []

def main():
    for a in work_data:
        temp_time = time_finder(a)
        if not result:
            result.append(str(temp_time) + ",")
        if on_off_finder(a):
            result[-1] += set_bit(True, note_finder(a))
        elif not on_off_finder(a):
            result[-1] += set_bit(True, note_finder(a))
        elif time_finder_comm(result[-1]) == temp_time:
            result[-1] = str(temp_time) + "," + set_bit_prev(on_off_finder(a), note_finder(a), -1)
        elif time_finder_comm(result[-1]) != temp_time:
            result.append(str(temp_time) + "," + set_bit_prev(on_off_finder(a), note_finder(a), -1))
    for b in result:
        output_file.write(b)
        output_file.write("\n")
    output_file.close()

def set_bit(On, note):
    if 21 <= note <= 28 and On:
        return str(2**(note - 21)) + ",0,0,0,0,0,0,0,0,0,0"
    elif 29 <= note <= 36 and On:
        return "0," + str(2**(note - 29)) + ",0,0,0,0,0,0,0,0,0"
    elif 37 <= note <= 44 and On:
        return "0,0," + str(2**(note - 37)) + ",0,0,0,0,0,0,0"
    elif 45 <= note <= 52 and On:
        return "0,0,0," + str(2**(note - 45)) + ",0,0,0,0,0,0"
    elif 53 <= note <= 60 and On:
        return "0,0,0,0," + str(2**(note - 53)) + ",0,0,0,0,0"
    elif 61 <= note <= 68 and On:
        return "0,0,0,0,0," + str(2**(note - 61)) + ",0,0,0,0"
    elif 69 <= note <= 76 and On:
        return "0,0,0,0,0,0," + str(2**(note - 69)) + ",0,0,0"
    elif 77 <= note <= 84 and On:
        return "0,0,0,0,0,0,0," + str(2**(note - 77)) + ",0,0"
    elif 85 <= note <= 92 and On:
        return "0,0,0,0,0,0,0,0," + str(2**(note - 85)) + ",0"
    elif 93 <= note <= 100 and On:
        return "0,0,0,0,0,0,0,0,0," + str(2**(note - 93)) + ",0"
    elif 101 <= note <= 108 and On:
        return "0,0,0,0,0,0,0,0,0,0," + str(2**(note - 101))
    else:
        return "0,0,0,0,0,0,0,0,0,0,0"

def set_bit_prev(On, note, index):
    temp = result[index]
    temp = temp[temp.find(",")+1:]
    if 21 <= note <= 28:
        local_temp = temp[:temp.find(",")]
        if On:
            return str(int(local_temp) + (2**(note - 21))) + temp[temp.find(","):]
        else:
            return str(int(local_temp) - (2**(note - 21))) + temp[temp.find(","):]
    elif 29 <= note <= 36:
        local_temp = temp[temp.find(",")+1:indexTh(temp, ",", 2)]
        if On:
            return temp[:temp.find(",")+1] + str(int(local_temp) + (2**(note - 29))) + temp[indexTh(temp, ",", 2):]
        else:
            return temp[:temp.find(",")+1] + str(int(local_temp) - (2**(note - 29))) + temp[indexTh(temp, ",", 2):]
    elif 37 <= note <= 44:
        local_temp = temp[indexTh(temp, ",", 2)+1:indexTh(temp, ",", 3)]
        if On:
            return temp[:indexTh(temp, ",", 2)+1] + str(int(local_temp) + (2**(note - 37))) + temp[indexTh(temp, ",", 3):]
        else:
            return temp[:indexTh(temp, ",", 2)+1] + str(int(local_temp) - (2**(note - 37))) + temp[indexTh(temp, ",", 3):]
    elif 45 <= note <= 52:
        local_temp = temp[indexTh(temp, ",", 3)+1:indexTh(temp, ",", 4)]
        if On:
            return temp[:indexTh(temp, ",", 3)+1] + str(int(local_temp) + (2**(note - 45))) + temp[indexTh(temp, ",", 4):]
        else:
            return temp[:indexTh(temp, ",", 3)+1] + str(int(local_temp) - (2**(note - 45))) + temp[indexTh(temp, ",", 4):]
    elif 53 <= note <= 60:
        local_temp = temp[indexTh(temp, ",", 4)+1:indexTh(temp, ",", 5)]
        if On:
            return temp[:indexTh(temp, ",", 4)+1] + str(int(local_temp) + (2**(note - 53))) + temp[indexTh(temp, ",", 5):]
        else:
            return temp[:indexTh(temp, ",", 4)+1] + str(int(local_temp) - (2**(note - 53))) + temp[indexTh(temp, ",", 5):]
    elif 61 <= note <= 68:
        local_temp = temp[indexTh(temp, ",", 5)+1:indexTh(temp, ",", 6)]
        if On:
            return temp[:indexTh(temp, ",", 5)+1] + str(int(local_temp) + (2**(note - 61))) + temp[indexTh(temp, ",", 6):]
        else:
            return temp[:indexTh(temp, ",", 5)+1] + str(int(local_temp) - (2**(note - 61))) + temp[indexTh(temp, ",", 6):]
    elif 69 <= note <= 76:
        local_temp = temp[indexTh(temp, ",", 6)+1:indexTh(temp, ",", 7)]
        if On:
            return temp[:indexTh(temp, ",", 6)+1] + str(int(local_temp) + (2**(note - 69))) + temp[indexTh(temp, ",", 7):]
        else:
            return temp[:indexTh(temp, ",", 6)+1] + str(int(local_temp) - (2**(note - 69))) + temp[indexTh(temp, ",", 7):]
    elif 77 <= note <= 84:
        local_temp = temp[indexTh(temp, ",", 7)+1:indexTh(temp, ",", 8)]
        if On:
            return temp[:indexTh(temp, ",", 7)+1] + str(int(local_temp) + (2**(note - 77))) + temp[indexTh(temp, ",", 8):]
        else:
            return temp[:indexTh(temp, ",", 7)+1] + str(int(local_temp) - (2**(note - 77))) + temp[indexTh(temp, ",", 8):]
    elif 85 <= note <= 92:
        local_temp = temp[indexTh(temp, ",", 8)+1:indexTh(temp, ",", 9)]
        if On:
            return temp[:indexTh(temp, ",", 8)+1] + str(int(local_temp) + (2**(note - 85))) + temp[indexTh(temp, ",", 9):]
        else:
            return temp[:indexTh(temp, ",", 8)+1] + str(int(local_temp) - (2**(note - 85))) + temp[indexTh(temp, ",", 9):]
    elif 93 <= note <= 100:
        local_temp = temp[indexTh(temp, ",", 9)+1:indexTh(temp, ",", 10)]
        if On:
            return temp[:indexTh(temp, ",", 9)+1] + str(int(local_temp) + (2**(note - 93))) + temp[indexTh(temp, ",", 10):]
        else:
            return temp[:indexTh(temp, ",", 9)+1] + str(int(local_temp) - (2**(note - 93))) + temp[indexTh(temp, ",", 10):]
    elif 101 <= note <= 108:
        local_temp = temp[indexTh(temp, ",", 10)+1:]
        if On:
            return temp[:indexTh(temp, ",", 10)+1] + str(int(local_temp) + (2**(note - 101)))
        else:
            return temp[:indexTh(temp, ",", 10)+1] + str(int(local_temp) - (2**(note - 101)))

def indexTh(in_string, find_this, th):
    order = 1
    last_index = 0
    while True:
        temp = in_string.find(find_this, last_index)
        if temp == -1:
            return -1
        if order == th:
            return temp
        order += 1
        last_index = temp + 1

def time_finder(in_string):
    time_end = in_string.index(" ")
    return int(in_string[:time_end])

def time_finder_comm(in_string):
    time_end = in_string.index(",")
    return int(in_string[:time_end])

def note_finder(in_string):
    num_start = in_string.index("n=") + 2
    num_end = in_string.index("v=") - 1
    return int(in_string[num_start:num_end])

def on_off_finder(in_string):
    start = in_string.index(" ") + 1
    end = in_string.index("ch=") - 1
    if in_string[start:end] == "On":
        return True
    elif in_string[start:end] == "Off":
        return False

main()

Processing-Skript zum Senden an den Arduino

import processing.serial.*;

Serial myPort;
String[] inputLines;

void setup() {
  myPort = new Serial(this, "COM3", 9600);
  inputLines = loadStrings("translated.txt");
  run();
}

void run() {
  int lastTime = 0;
  for (int i = 0; i < inputLines.length; i++) {
    String temp = inputLines[i];
    delay((Integer.parseInt(temp.substring(0, temp.indexOf(","))) - lastTime) * 5);
    lastTime = Integer.parseInt(temp.substring(0, temp.indexOf(",")));
    send(temp.substring(temp.indexOf(",") + 1));
  }
}

void send(String data) {
  String eleventh = data.substring(0, indexOforder(data, ",", 1));
  String tenth = data.substring(indexOforder(data, ",", 1)+1, indexOforder(data, ",", 2));
  String ninth = data.substring(indexOforder(data, ",", 2)+1, indexOforder(data, ",", 3));
  String eighth = data.substring(indexOforder(data, ",", 3)+1, indexOforder(data, ",", 4));
  String seventh = data.substring(indexOforder(data, ",", 4)+1, indexOforder(data, ",", 5));
  String sixth = data.substring(indexOforder(data, ",", 5)+1, indexOforder(data, ",", 6));
  String fifth = data.substring(indexOforder(data, ",", 6)+1, indexOforder(data, ",", 7));
  String forth = data.substring(indexOforder(data, ",", 7)+1, indexOforder(data, ",", 8));
  String third = data.substring(indexOforder(data, ",", 8)+1, indexOforder(data, ",", 9));
  String second = data.substring(indexOforder(data, ",", 9)+1, indexOforder(data, ",", 10));
  String first = data.substring(indexOforder(data, ",", 10)+1);

  myPort.write("888f");
  myPort.write(first + "f");
  myPort.write(second + "f");
  myPort.write(third + "f");
  myPort.write(forth + "f");
  myPort.write(fifth + "f");
  myPort.write(sixth + "f");
  myPort.write(seventh + "f");
  myPort.write(eighth + "f");
  myPort.write(ninth + "f");
  myPort.write(tenth + "f");
  myPort.write(eleventh + "f");
  myPort.write("999f");
}

int indexOforder(String data, String find, int order) {
  int currentOrder = 0;
  int lastLocation = 0;
  while (currentOrder < order) {
    lastLocation = data.indexOf(find, lastLocation + 1);
    currentOrder += 1;
  }
  return lastLocation;
}

Arduino-Code

Einfacher Code für den Arduino. Empfängt Daten über Serial. 888 und 999 sind reserviert für Schieberegister-Öffnen/Schließen.

Keine Vorschau verfügbar (nur Download).

Schaltplan

Entschuldigen Sie die unprofessionelle Zeichnung. Das Konzept entspricht im Wesentlichen dem Arduino ShiftOut-Beispiel, nur mit MOSFETs. Ich empfehle, auch dort hineinzuschauen.

PiBot: Arduino-gesteuerter Klavierroboter für präzise Musikwiedergabe

Herstellungsprozess

  1. Bluetooth-gesteuerter Raspberry Pi Roboter mit Audio-Feedback bauen
  2. Internet-gesteuerter Video-Streaming-Roboter mit Arduino & Raspberry Pi bauen
  3. WLAN-gesteuerter Raspberry Pi Roboter mit Python bauen – Schritt-für-Schritt
  4. LEDs mit Alexa über Raspberry Pi steuern – Einfache Schritt-für-Schritt-Anleitung
  5. Arduino Iron Man bauen: Komponenten, Sensoren & Schritt-für-Schritt-Anleitung
  6. Find Me: Smarter Gegenstands-Finder mit Arduino und Bluetooth
  7. DIY Arduino Luftbefeuchter-Steuerung mit Relais – Sichere Hochspannungs-Installation
  8. Sprachgesteuerter Roboter mit Arduino Nano bauen
  9. MobBob: Eigenen Arduino-Roboter bauen, per Android-Smartphone steuern
  10. DIY Arduino Lenkrad / Joystick für Rennspiele – USB-HID-Umbau