电机在唱歌
黄老咩
编辑于 2021年01月26日 16:04

cut-off

前段时间看了《春物》,感慨颇深啊

情节方面就不多说了(认真

OP和ED也很好听

好几年前看到有人用步进电机放音乐,可是想法就一直那样搁置在一边

可能因为看了春物产生了一些触动,勾起了我多年以前的想法(多么奇怪的联系

https://www.bilibili.com/video/BV1Gv4y1f7yC?p=5​

视频在上面↑↑↑↑↑↑

五台42步进电机

cut-off

先科普一下步进电机的大致原理: 步进电机给一个脉冲就走一步(一个角度,叫步距角或者步进角,比如图上一个步进角为15°),但是脉冲的频率决定了步进电机步进的快慢。

来源https://zhuanlan.zhihu.com/p/65076179

正是这个原因,当电机接收不同频率的脉冲时,会发出不同的声音,便可以以此为切入点,把电机当成喇叭使(真·动铁

cut-off

这几天在网络搜集相关资料和视频,发现大多数都是一个步进电机配一块类似于这种老贵的驱动器

从某宝截来的图

或者用一块leonardo配上四个nano

来源https://zhuanlan.zhihu.com/p/151240997

吃土玩家落下激动的泪水

直到在某宝逛,发现有cnc shield v3这种东西,立马来劲了

又是从某宝截来的图

可以直接插在uno上,太美妙了

该扩展板可用作雕刻机,3D打印机等的驱动扩展板,一共有4路步进电机驱动模块的插槽,可驱动4路步进电机,而每一路步进电机都只需要2个IO口,也就是说,6个IO口就可以很好的管理3个步进电机,使用起来非常的方便,告别传统步进电机操作繁琐。

当然也是要驱动模块的,一般有A4988和DRV8825,我这里选了后者

某宝卖的8825模块

8825引脚图

若要控制四路电机,只要在这里套上俩跳线帽

A就可以像XYZ被独立控制了

就是这里的俩跳线帽

v3对应于uno管脚是这样的,但实际我这里9、10、11脚用不上

来源https://wenku.baidu.com/view/6542c342195f312b3169a5f7.html

就把v3上面的这三个脚用烙铁取下来了,因为后期要用uno上面的口

这三个脚

重点来了!!!

我有了uno 

有了8825

有了电机

怎么让电机唱歌呢?

有一种音频格式叫.mid

MIDI(Musical Instrument Digital Interface)乐器数字接口 ,是20 世纪80 年代初为解决电声乐器之间的通信问题而提出的。MIDI是编曲界最广泛的音乐标准格式,可称为"计算机能理解的乐谱"。它用音符的数字控制信号来记录音乐。一首完整的MIDI音乐只有几十KB大,而能包含数十条音乐轨道。几乎所有的现代音乐都是用MIDI加上音色库来制作合成的。MIDI 传输的不是声音信号, 而是音符、控制参数等指令, 它指示MIDI 设备要做什么,怎么做, 如演奏哪个音符、多大音量等。

而每个音符都有它对应的频率,之前我们讲什么来着,步进电机给不同频率的脉冲会发出不同的音调,女少口阿

你可以自己用音乐制作软件制作你的midi音乐,也可以直接下载

但是我们拿到的.mid格式的音乐包含的每个音符的信息怎么获取呢?

这里我们要用到matlab里面的一个叫 miditoolbox 的函数库(别人写的)

下载链接:https://github.com/miditoolbox/1.1

写这个函数库的人也是很厉害啊,manual就有90多页

稍微翻一翻,里面有很多有趣的函数

可以解析出相应的信息

在matlab里面把这个文件夹添加到路径

然后输入类似这样的命令行

代码块
C++
自动换行
复制代码
nmat=readmidi('你的midi音频路径');
nmat(:,4)=midi2hz(pitch(nmat));
复制成功

输入命令行

 

会得到7列结果

说明文档里的

分别是:开始时间(单位:节拍)               持续时间(单位:节拍)               音轨(0~16)               音调               音量(0~127)               开始时间(单位:秒)               持续时间(单位:秒)

但对于我们来说,只要音调、开始时间和持续时间就足够了,这足够记录每一个音符的信息

也就是第四列和倒数俩列

同时我们也看下有多少个音符被记录

有些时候这个工具会有点问题,某些音符识别不出来,但总体而言不碍事

这时候新建一个记事本,把数据存在里面

重点又来了!!!

我们光有音符信息没用啊,我们需要把音符分配给电机才算是完结呀

但如何分配呢

比如我有这几个音符

我们需要针对每个音符的起始时间作为一个时间点,相邻俩个时间点就是一段区间

下面数字是当前区间有几个音符

因为一个电机在一个时间内只能发出一种声音,所以只能一个个分配,

如果某个区间音符太多我们只能有所取舍,比如我就是从高音往下取。

我的实现代码缩略

比如我处理这个第三季的OP 芽ぐみの雨

一段处理gif

分好了区间和每个区间都有那些音符

接着

输出处理好的数据,比如我这里有五个电机,

分别输出

    持续时间、x电机速度、y电机速度、z电机速度、a电机速度和m电机频率(稍后解释)

输出数据到文本

这时我们打开arduino

创建一个结构体

存放数据们

为了充分用上有限的空间,结构体只有一个float,而且用了PROGMEM

arduino ide实现部分要感谢:https://www.jianshu.com/p/7d4b27484d25

提供了宝贵的思路,所以我也决定开源!

重点又双叒叕来了!!!

 Arduino UNO有三个定时器,分别是timer0,timer1和timer2。每个定时器都有一个计数器,在计时器的每个时钟周期递增。当计数器达到存储在比较匹配寄存器中指定值时触发CTC定时器中断。一旦定时器计数器达到该值,它将在定时器时钟的下一个定时器上清零(复位为零),然后它将继续再次计数到比较匹配值。通过选择比较匹配值并设置定时器递增计数器的速度,你可以控制定时器中断的频率。

说实话一开始我也不是很懂

建议看一下:https://blog.csdn.net/redeemer_Qi/article/details/80970447

后来参看了一些资料,大致有了一点认识(这块有亿点复杂,去看了datasheet才略知一二

Timer0:PIN5和PIN6的PWM输出、delay()、millis()、delayMicroseconds()。 Timer1:PIN9和PIN10的PWM输出、舵机库Servo.h、TimerOne库、使用两个tone()变量时。 Timer2:PIN3和PIN11的PWM输出、无源蜂鸣器的tone()、红外库IRremote.h默认使用Timer2,可以通过修改库文件使用Timer1.

因为我要控制五路电机,有四路通过timer1输出PWM

一路通过tone();函数输出,但是具体传入的参数不太一样

C艹实现部分因为用了不少指针,一开始各种堆栈错误,内存泄漏(wu

用单步调试在那边一通乱搞,总算搞出了点名堂(doge,所以还不三连吗,懂?(doge

我也不是很精通c++,可能还有一些bug,望各位大大们谅解

代码部分全都上传百度云啦

链接:https://pan.baidu.com/s/1eDO4zqtO2myfYsTT9XgqOg 

提取码:wacw 

玩的开心!

cut-off

ps:在arduino实现部分有一些注释

       不过我还是放出ATmega328p对应uno管脚的图片以供参考

引脚图

cut-off

悄咪咪的放一张大老师限定

太美了呀

第一次写专栏可能写的不是很好

如有错误还请指出呀

cut-off

部分参考资料(排名不分先后): ●https://blog.csdn.net/weixin_39962285/article/details/110804665?ops_request_misc=%25257B%252522request%25255Fid%252522%25253A%252522161102060716780255210273%252522%25252C%252522scm%252522%25253A%25252220140713.130102334.pc%25255Fall.%252522%25257D&request_id=161102060716780255210273&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-20-110804665.pc_search_result_cache&utm_term=arduino%208825 ●https://www.jianshu.com/p/cef09d3411ab ●https://blog.csdn.net/weixin_30342209/article/details/96725854?utm_medium=distribute.pc_relevant_download.none-task-blog-baidujs-1.nonecase&depth_1-utm_source=distribute.pc_relevant_download.none-task-blog-baidujs-1.nonecase ●https://blog.csdn.net/qq_30209977/article/details/106672170?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160223441119725222407690%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=160223441119725222407690&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v3~pc_rank_v2-3-106672170.first_rank_ecpm_v3_pc_rank_v2&utm_term=arduino+%E4%BF%AE%E6%94%B9tone+%E5%AE%9A%E6%97%B6%E5%99%A8&spm=1018.2118.3001.4187 ●https://blog.csdn.net/acktomas/article/details/89227491 ●https://www.jianshu.com/p/7d4b27484d25 ●https://blog.csdn.net/watson2016/article/details/52924464 ●https://blog.csdn.net/weixin_30342209/article/details/96725854?utm_medium=distribute.pc_relevant_download.none-task-blog-baidujs-1.nonecase&depth_1-utm_source=distribute.pc_relevant_download.none-task-blog-baidujs-1.nonecase ●https://wenku.baidu.com/view/6542c342195f312b3169a5f7.html ●http://www.diy-robots.com/?p=852 ●https://zhidao.baidu.com/question/198518320932625085.html ●https://blog.csdn.net/chenhezhuyan/article/details/7985942?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control ●https://www.arduino.cn/forum.php?mod=viewthread&tid=80668 ●https://www.arduino.cn/thread-83019-1-1.html 网页链接​ ●https://blog.csdn.net/sdlgq/article/details/88720706?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161138947716780299039663%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=161138947716780299039663&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-3-88720706.pc_search_result_cache&utm_term=pgm_