Arduino 控制的钢琴机器人:PiBot
组件和用品
| × | 1 | ||||
| × | 11 | ||||
| × | 88 | ||||
| × | 2 | ||||
| × | 11 | ||||
| × | 1 | ||||
| × | 11 | ||||
| × | 11 | ||||
| × | 88 | ||||
| × | 88 |
必要的工具和机器
|
应用和在线服务
| ||||
| ||||
|
关于这个项目
如何开始:
许多年前,雅马哈推出了自动钢琴。年少无知的我,在一家乐器店的玻璃窗后面,看到了那架钢琴演奏的音乐。
闲聊够了,我开始这个项目真的没有什么大的理由,除了我只是想这么做。
概述:
一块 Arduino Mega 的单板成本约为 40 美元,需要两块板来控制 88 个螺线管。那相当昂贵。相反,获得一个便宜的 Arduino Uno 和 11 个移位寄存器。移位寄存器是一种用少量输出引脚控制许多输出(通常是 LED)的方法。本质上,它是一个带有 11 个移位寄存器和控制 88 个电磁阀的 Arduino。
由于我们使用移位寄存器,PC 将向 Arduino 而不是 MIDI com 发送一组位。 MIDI 文件将事先被翻译成一组比特。
硬件:
当我直接从中国拿到螺线管时,我发现这些螺线管的强度不足以推动钢琴键。当然,从最里面推钢琴键需要更大的力,但我认为这是不会损坏钢琴的最佳方法。最终,我通过 12V 螺线管推动了 24V 以获得足够的电力。
88 电磁阀耗电量大,因为买不起昂贵的电源,我决定用我爸爸的汽车电池。 (猜猜他现在哪儿也不去!)
这样一来,移位寄存器和 MOSFET 中的每一个都将安装在一个控制器板上。
595 右边有一个插座,以防我烧掉它。 (我做过一次。)电路图与此处的示例 2 完全相同。用 MOSFET 栅极替换 LED。如您所见,没有下拉电阻器,因为额外的电阻器会增加成本,并且将它们焊接在该板上会融化我的手指。从更严重的方面来说,这些 MOSFET 将在 5V 时打开并在 4V 左右时关闭。通过无数小时的测试证实了这一点。 (理论上不正确。不要听我的。)
最后,拿一块塑料板将螺线管粘在上面。考虑到它会变热,使用热胶和塑料板是一个坏主意,但这是我能负担得起的最好的。
然后将电磁线圈的一侧连接到电池的正极。
软件:
第一步是获取一个midi文件。
第二步,将midi转成文本形式。这可以在这个方便的网站上完成:http://flashmusicgames.com/midi/mid2txt.php。
为简单起见,忽略拍号、速度和标准杆。速度可以乘以稍后的时间。基本上你想要一个这样的文件:
现在,通过 Python 代码(附后)运行它,使用它创建 11 组 8 位数据随时间变化。
它们已准备好通过处理 COM 发送到 Arduino。
请参阅附件以了解处理如何发送这些数据以及 Arduino 如何处理它们。
*注意:我的编码习惯很差,可能很难阅读。处理从右向左发送数据,因为 Arduino 在物理钢琴上将数据向右推送。
这东西有效:
故障排除:
如果专业工程师看到这个帖子,他会认为整个系统会有很多问题。还有很多问题。
由于螺线管是热粘在板上的,因此螺线管过热和熔化热胶是一个大问题。解决方案是简单地将它们取下并用可承受高达 150C 的双面胶带替换。
代码
- 用于翻译的 Python 代码
- 处理向arduino发送数据
- Arduino 代码
用于翻译的 Python 代码Python
获取文本化的 mifi 文件并将其转换为 11 组二进制文件供 arduino 获取。output_file =open("translated.txt", "w")input_file =open("megalocania.txt")raw_input_data =input_file.read ()work_data =raw_input_data.splitlines()result =[]#output_file =open("result.txt", "w")def main():for a in work_data:temp_time =time_finder(a) if 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(note>=21 and note <=28 and On):return str(2**(note - 21)) + ",0,0,0,0,0,0,0,0,0 ,0" elif(note>=29 and note <=36 and On):return "0," + str(2**(note - 29)) + ",0,0,0,0,0,0, 0,0,0" elif(note>=37 and note <=44 and On):return "0,0," + str(2**(note - 37)) + ",0,0,0,0 ,0,0,0,0" elif(note>=45 and note <=52 and On):return "0,0,0," + str(2**(note - 45)) + ",0, 0,0,0,0,0,0" elif(note>=53 and note <=60 and On):return "0,0,0,0," + str(2**(note - 53)) + ",0,0,0,0,0,0" elif(note>=61 and note <=68 and On):return "0,0,0,0,0,0," + str(2**( note - 61)) + ",0,0,0,0,0" elif(note>=69 and note <=76 and On):return "0,0,0,0,0,0," + str (2**(note - 69)) + ",0,0,0,0" elif(note>=77 and note <=84 and On):return "0,0,0,0,0,0, 0," + str(2**(note - 77)) + ",0,0,0" elif(note>=85 and note <=92 and On):return "0,0,0,0,0 ,0,0,0," + str(2**(note - 85)) + ",0,0" elif(note>=93 and note <=100 and On):return "0,0,0, 0,0,0,0,0,0," + str(2**(note - 93)) + ",0" elif(note>=101 and note <=108 and On):返回 "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):#与 set_bit 相同,但之前知道 temp =result[index] temp =temp[(temp.find (",") + 1):] if(note>=21 and note <=28):local_temp =temp[0:temp.find(",")] if(On):return str(int(local_temp) + (2**(note - 21))) + temp[temp.find(","):] if(not On):return str(int(local_temp) - (2**(note - 21))) + temp[temp.find(","):] elif(note>=29 and note <=36):local_temp =temp[(temp.find(",") + 1):indexTh(temp, "," , 2)] if(On):return temp[0:temp.find(",") + 1] + str(int(local_temp) + (2**(note - 29))) + temp[indexTh(temp , ",", 2):] if(not On):return temp[0:temp.find(",") + 1] + str(int(local_temp) - (2**(note - 29))) + temp[indexTh(temp, ",", 2):] elif(note>=37 and note <=44):local_temp =temp[(indexTh(temp, ",", 2) + 1):indexTh(temp , ",", 3)] if(On):return temp[0:indexTh(temp, ",", 2) + 1] + str(int(local_temp) + (2**(note - 37))) + 温度[输入dexTh(temp, ",", 3):] if(not On):return temp[0:indexTh(temp, ",", 2) + 1] + str(int(local_temp) - (2**(note) - 37))) + temp[indexTh(temp, ",", 3):] elif(note>=45 and note <=52):local_temp =temp[(indexTh(temp, ",", 3) + 1 ):indexTh(temp, ",", 4)] if(On):return temp[0:indexTh(temp, ",", 3) + 1] + str(int(local_temp) + (2**(注意- 45))) + temp[indexTh(temp, ",", 4):] if(not On):return temp[0:indexTh(temp, ",", 3) + 1] + str(int(local_temp) ) - (2**(note - 45))) + temp[indexTh(temp, ",", 4):] elif(note>=53 and note <=60):local_temp =temp[(indexTh(temp, ",", 4) + 1):indexTh(temp, ",", 5)] if(On):return temp[0:indexTh(temp, ",", 4) + 1] + str(int(local_temp) ) + (2**(note - 53))) + temp[indexTh(temp, ",", 5):] if(not On):return temp[0:indexTh(temp, ",", 4) + 1] + str(int(local_temp) - (2**(note - 53))) + temp[indexTh(temp, ",", 5):] elif(note>=61 and note <=68):local_temp =temp[(indexTh(temp, ",", 5) + 1):indexTh(temp, ",", 6)] if(On):return temp[0:indexTh(temp, ",", 5) + 1 ] + str(int(local_temp) + (2**(note - 61))) + temp[indexTh(temp, ",", 6):] if(not On):return temp[0:indexTh(temp, ",", 5) + 1] + str(int(local_temp) - (2**(note - 61))) + temp[indexTh(temp, ",", 6):] elif(note>=69 and note <=76):local_temp =temp[(indexTh(temp, ",", 6) + 1):indexTh(temp, ",", 7)] if(On):return temp[0:indexTh(temp, ",", 6) + 1] + str(int(local_temp) + (2**(note - 69))) + temp[indexTh(temp, ",", 7):] if(not On):return temp[0:indexTh(temp, ",", 6) + 1] + str(int(local_temp) - (2**(note - 69))) + temp[indexTh(temp, ",", 7):] elif(note>=77 and note <=84):local_temp =temp[(indexTh(temp, ",", 7) + 1):indexTh(temp, ",", 8)] if(On):return temp[0:indexTh(temp, ",", 7) + 1] + str(int(local_temp) + (2**(note - 77))) + temp[indexTh(temp, ",", 8):] if(not On):return temp[0:indexTh(temp, ",", 7) + 1] + str(int(local_temp) - (2**(note - 77))) + temp[indexTh(temp) , ",", 8):] elif(note>=85 and note <=92):#error here local_temp =temp[(indexTh(temp, ",", 8) + 1):indexT h(temp, ",", 9)] if(On):return temp[0:indexTh(temp, ",", 8) + 1] + str(int(local_temp) + (2**(note - 85) ))) + temp[indexTh(temp, ",", 9):] if(not On):return temp[0:indexTh(temp, ",", 8) + 1] + str(int(local_temp) - (2**(note - 85))) + temp[indexTh(temp, ",", 9):] elif(note>=93 and note <=100):local_temp =temp[(indexTh(temp, ", ", 9) + 1):indexTh(temp, ",", 10)] if(On):return temp[0:indexTh(temp, ",", 9) + 1] + str(int(local_temp) + (2**(note - 93))) + temp[indexTh(temp, ",", 10):] if(not On):return temp[0:indexTh(temp, ",", 9) + 1] + str(int(local_temp) - (2**(note - 93))) + temp[indexTh(temp, ",", 10):] elif(note>=101 and note <=108):local_temp =temp [(indexTh(temp, ",", 10) + 1):] if(On):return temp[0:indexTh(temp, ",", 10) + 1] + str(int(local_temp) + (2) **(note - 101))) if(not On):return temp[0:indexTh(temp, ",", 10) + 1] + str(int(local_temp) - (2**(note - 101) )) def indexTh(in_string, find_this, th):#取字符串,要查找的字符串,以及要查找的字符串的顺序该顺序 #returns index 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 + 1def time_finder(in_string):#取一个字符串并找到时间,以int形式返回 time_end =in_string.index(" ") return int(in_string[0:time_end])def time_finder_comm(in_string):#取字符串求时间,以int逗号返回 time_end =in_string.index(",") return int(in_string[0:time_end]) def note_finder(in_string):#取字符串,查找n=,返回 n 值作为 int 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):#takes一个字符串,查找 On 或 Off,如果 On start =in_string.index(" ") + 1 end =in_string.index("ch=") - 1 if in_string[start:end] =="On",则返回 true: return True elif in_string[start:end] =="Off":return Falsemain()
向arduino发送数据的处理处理
读取翻译后的文本文件并将其发送到 arduino。如果速度不同于 50000,则必须修改速度乘数。
反转字节,因为它从左向右移动。 (文本文件假设从右到左)
import processing.serial.*;Serial myPort;String[] inputLines;void setup(){ myPort =new Serial(this, "COM3", 9600); inputLines =loadStrings("translated.txt"); run();}void run(){ //使用数据方法读取时间并发送数据bt行 int lastTime =0; for(int i =0; iArduino 代码Arduino
arduino 的简单代码。从串行获取输入。 888 和 999 为移位寄存器打开和关闭命令保留。无预览(仅下载)。
示意图
不专业的画图请见谅。这是整个概念。除了 mosfet 之外,Arduino -ShiftOut 文档的图表之间没有区别。我也建议看那个。制造工艺