ds-dccデコーダーをVVVF音っぽい音が出るPWMコントローラにする [ds-DCCデコーダ]
題名だが、今回、本当にやりたかったことは、「ds-DCCデコーダをVVVF音が出るデコーダにする」だ。しかし、ちょっとゴールが遠かった。だから、まだ道半ば。
DCC(デジタル・コマンド・コントロール)館 様の
DCCからでも、VVVF音 をds-DCCデコーダでもやりたかったが、一日では無理だった。
ステップとしては、
(1)モーターから音をだせる事を確認する。
(2)モーターから音階を出せるようにする。
(3)VVVF音チック名PWMコントローラを作る。
(4)(3)のソースをds-DCCデコーダに乗せれば、VVVF音を奏でるモーター車が完成という流れだ。
で、今回は(3)まで出来たつもり。
ことの発端は、「ds-dccデコーダでVVVF音出そうと思っております」とYaasan様に連絡したら、
------------------
VVVF風ですが、TIMER1のモードをいじって、カウンタMAX値を調整すると、でき
ると思いますが今のところ試しておりません。
低速時はキャリア周波数を低めに、加速しながら速度に応じて上げていって高速
時は高くすると実物に近くなります。
------------------
とのことで、できそうだと思ったこと。
<(1)モーターから音をだせる事を確認する。>
音を出すだけなら、一番簡単なのはAnalogWriteを使うことである。PWM周期は約 490Hzとのことで、Dutyだけ変化させると、音階は変わらないのに、モーターのスピードが変わることを確認した。
具体的には、以下のスケッチを使っている。
---------------------------
---------------------------
接続は、ds-dccデコーダの入力にcmdr-arduino記事同様に12VのAcアダプタ出力をいれ、出力側に適当なモーターを接続(今回はマブチモーター3V用を使用。12Vだし危ないと思います。)。
ピーピーとモーターが鳴るのはわかったので、OK。なお上記のスケッチはアクセサリーデコーダのものをなんとなく流用している。
操作は、ツール→シリアルモニタから行い
"]":0.3秒間正転方向で音が出る
"[":0.3秒間逆転方向で音が出る
ソースの"analogWrite(MOTOR_PWM_B, 25);"などの25をもっと大きな数にすればDutyが大きくなるため、モーターは回りやすくなる。ただし255(上限)にすると、直流でHighなので今回的には(PWMにしたいわけだし)意味が無い。
<(2)モーターから音階を出せるようにする。>
ここで、Timer1が必要になる。Timer1はArduinoからusオーダーでPWM波形を出せるライブラリとのことで、パルス幅、Dutyを調整できるため、周波数を変えることで音の高さを変えられるし、Dutyを変えることでモーターの速度を変えられる。そして、DS-DCCデコーダのドライバ出力ポート(D9,D10)に出力できる。
で、実際に音の高さ(PWMの周波数)、モーターのスピード(Duty)を変えて、モーター車がどのように動くかを実験するために、スケッチを書いた。(以下)
-----------------------
-----------------------
なんか、ソースが長いが、
まず、TimerOneのライブラリを落としてきて、登録しておく必要がある。いくつかバージョンがあるようだが、わたしはTimer_One_r11を使用した。(違いは知らない)
接続はcmdr_arduinoとまったく同じで、入力側に12VACアダプタを接続し、出力側をフィーダーにして線路につないでおく。線路には壊れても惜しくない車両(わたしだと鉄コレ15m級のモーター車。動きがとっても悪いから)。下は写真。ただしこの写真ではKato113系のモーター車だが。
操作は、ツール→シリアルモニタから行い
"]":モーター正転
"[":モーター逆転
"0":モーター非常停止(というか停止)
"2":PWMの周波数を半音上げる。
"1":PWMの周波数を半音下げる。
"4":PWMのDutyを+8する。(上限1023)
"3":PWMのDutyを-8する。(上限1023)
音の周波数については、ここを参考にした。表から、半音上げるためには周波数を1.05倍すればよいらしい。
これで動かしてわかったことは、モーターから音はするけど、回転しないとかがよくあり、パラメータが難しいかもと思ったことである。
適当な周波数に固定し、Dutyだけ変えるとE231系みたいな走り出し音になる。とかいっぱい遊んでしまって、時間が経ってしまった・・・。
音自体は、電車のモーター音のページ 様のところでいっぱい聞いてみたが、ちょっと奥が深くて、おなかがいっぱいになってしまった。何かに忠実なものにするのはやめようと心に誓った。
で、ここで、NゲージでVVVF音を出す動画を探そうと検索したら、しろくま 様の記事があり、動画をみて、「あ、こういうことしたい!」と横道にそれてしまうことにした。
<(3)VVVF音風なPWMコントローラを作る。>
で、ここまで作った後に、VVVFってそういえばどう音階が切り替わるんですか?と思い検索したら、使えそうなページを二つ見つけられた。
「1.車両用パワーデバイスのGTO、IGBT 3レベル回路とは 今後の素子は?」で、「3. IGBTと非同期PWM制御」のように周波数を変更してけば、本物っぽくなることがわかる。
で、具体的には、DCC(デジタル・コマンド・コントロール)館 様の記事の、周波数テーブルのようなものを作る必要があることがわかった。
そして、試行錯誤しながら、最終的に以下のスケッチを作った。
-------------------------------
-------------------------------
システムは(2)と同じでcmdr-arduinoをds-dccデコーダーでやったときと配線は同じ。
操作は(2)同様 ツール→シリアルモニタで
"]":モーター車 正方向
"[":モーター車 逆方向
"0":モーター非常停止(というか停止)
"2":PWM Dutyのオフセットを+10する。(走り出しの調整)
"1":PWM Dutyのオフセットをー10する。(走り出しの調整)
"7":減速する。
"8":その速度で走る。
"9":加速する。
ソースだが、kasoku[5][4]が、音階を変えるLUTのようなもの。
5段持っており、例えば一段めは、”{ 0, 30,300,500},”なので、スピードが0~30のときに音階を300Hz~500Hzにかえるという意味。
loco_speed_max は車両の最高速リミット(本当は1023まで取れる)
スピードが上がるとモーター音が大きくなり、VVVF風の音はほとんど聞こえなくなる。
loopの最後にある”delay(80)”がLoopの周期をほぼ決めており、ここを大きくすれば加減速が遅くなる。小さくするともちろん加減速が速くなる。
以下が動画。音を大きくしないと聞こえないかも。
DCC(デジタル・コマンド・コントロール)館 様の
DCCからでも、VVVF音 をds-DCCデコーダでもやりたかったが、一日では無理だった。
ステップとしては、
(1)モーターから音をだせる事を確認する。
(2)モーターから音階を出せるようにする。
(3)VVVF音チック名PWMコントローラを作る。
(4)(3)のソースをds-DCCデコーダに乗せれば、VVVF音を奏でるモーター車が完成という流れだ。
で、今回は(3)まで出来たつもり。
ことの発端は、「ds-dccデコーダでVVVF音出そうと思っております」とYaasan様に連絡したら、
------------------
VVVF風ですが、TIMER1のモードをいじって、カウンタMAX値を調整すると、でき
ると思いますが今のところ試しておりません。
低速時はキャリア周波数を低めに、加速しながら速度に応じて上げていって高速
時は高くすると実物に近くなります。
------------------
とのことで、できそうだと思ったこと。
<(1)モーターから音をだせる事を確認する。>
音を出すだけなら、一番簡単なのはAnalogWriteを使うことである。PWM周期は約 490Hzとのことで、Dutyだけ変化させると、音階は変わらないのに、モーターのスピードが変わることを確認した。
具体的には、以下のスケッチを使っている。
---------------------------
#define MOTOR_PWM_A 9 #define MOTOR_PWM_B 10 #define LED 13 void OutputStop(); void setup () { Serial.begin(115200); pinMode (MOTOR_PWM_A, OUTPUT); pinMode (MOTOR_PWM_B, OUTPUT); Serial.println("vvvf test"); } void loop () { if(Serial.available() > 0) { int ch = Serial.read(); //Serial.write(ch); switch (char(ch)) { case '[': { Serial.println("A1,B0"); OutputStop(); digitalWrite(MOTOR_PWM_B, 0); //digitalWrite(MOTOR_PWM_A, 1); analogWrite(MOTOR_PWM_A, 25); delay(300); OutputStop(); break; } case ']': { Serial.println("A0,B1"); OutputStop(); //digitalWrite(MOTOR_PWM_B, 1); analogWrite(MOTOR_PWM_B, 25); digitalWrite(MOTOR_PWM_A, 0); delay(300); OutputStop(); break; } default: break; } } } void OutputStop() { digitalWrite(MOTOR_PWM_A, LOW); digitalWrite(MOTOR_PWM_B, LOW); }
---------------------------
接続は、ds-dccデコーダの入力にcmdr-arduino記事同様に12VのAcアダプタ出力をいれ、出力側に適当なモーターを接続(今回はマブチモーター3V用を使用。12Vだし危ないと思います。)。
ピーピーとモーターが鳴るのはわかったので、OK。なお上記のスケッチはアクセサリーデコーダのものをなんとなく流用している。
操作は、ツール→シリアルモニタから行い
"]":0.3秒間正転方向で音が出る
"[":0.3秒間逆転方向で音が出る
ソースの"analogWrite(MOTOR_PWM_B, 25);"などの25をもっと大きな数にすればDutyが大きくなるため、モーターは回りやすくなる。ただし255(上限)にすると、直流でHighなので今回的には(PWMにしたいわけだし)意味が無い。
<(2)モーターから音階を出せるようにする。>
ここで、Timer1が必要になる。Timer1はArduinoからusオーダーでPWM波形を出せるライブラリとのことで、パルス幅、Dutyを調整できるため、周波数を変えることで音の高さを変えられるし、Dutyを変えることでモーターの速度を変えられる。そして、DS-DCCデコーダのドライバ出力ポート(D9,D10)に出力できる。
で、実際に音の高さ(PWMの周波数)、モーターのスピード(Duty)を変えて、モーター車がどのように動くかを実験するために、スケッチを書いた。(以下)
-----------------------
#include <TimerOne.h> #define MOTOR_PWM_A 9 #define MOTOR_PWM_B 10 #define LED 13 void OutputStop(); int pwm_duty = 32; double pwm_freq = 440; int pwm_a = 0;//pwm aは出力するか? int pwm_b = 0;//pwm bは出力するか? void setup () { //9,10は出力 pinMode (MOTOR_PWM_A, OUTPUT); pinMode (MOTOR_PWM_B, OUTPUT); //Serial使用 Serial.begin(115200); Serial.println("vvvf test K1"); //Timer1使用 Timer1.initialize(); //初期設定 int pwm_period = (double)1000000 / pwm_freq; Timer1.setPeriod(pwm_period); Timer1.setPwmDuty(MOTOR_PWM_A,pwm_duty * pwm_a); Timer1.setPwmDuty(MOTOR_PWM_B,pwm_duty * pwm_b); //PWMの開始 OutputStop(); } void loop () { if(Serial.available() > 0) { int ch = Serial.read(); //Serial.write(ch); switch (char(ch)) { case '2': { pwm_freq *= 1.05;//1.05倍で半音高くなる。 Serial.print("Freq+ :"); Serial.print(pwm_freq,DEC); Serial.print(" period :"); int pwm_period = (double)1000000 / pwm_freq; Serial.println(pwm_period,DEC); Timer1.setPeriod(pwm_period); break; } case '1': { pwm_freq /= 1.05;//1.05割って半音低くなる。 Serial.print("Freq- :"); Serial.print(pwm_freq,DEC); Serial.print(" period :"); int pwm_period = (double)1000000 / pwm_freq; Serial.println(pwm_period,DEC); Timer1.setPeriod(pwm_period); break; } case '4': { pwm_duty += 8; Serial.print("Duty(0-1023)+ :"); Serial.println(pwm_duty,DEC); Timer1.setPwmDuty(MOTOR_PWM_A,pwm_duty * pwm_a); Timer1.setPwmDuty(MOTOR_PWM_B,pwm_duty * pwm_b); break; } case '3': { pwm_duty -= 8; Serial.print("Duty(0-1023)- :"); Serial.println(pwm_duty,DEC); Timer1.setPwmDuty(MOTOR_PWM_A,pwm_duty * pwm_a); Timer1.setPwmDuty(MOTOR_PWM_B,pwm_duty * pwm_b); break; } case '0': { Serial.println("Stop"); OutputStop(); break; } case '[': { Serial.println("A1,B0"); pwm_a = 1; pwm_b = 0; Timer1.setPwmDuty(MOTOR_PWM_A,pwm_duty * pwm_a); Timer1.setPwmDuty(MOTOR_PWM_B,pwm_duty * pwm_b); //delay(300); //OutputStop(); break; } case ']': { Serial.println("A0,B1"); pwm_a = 0; pwm_b = 1; Timer1.setPwmDuty(MOTOR_PWM_A,pwm_duty * pwm_a); Timer1.setPwmDuty(MOTOR_PWM_B,pwm_duty * pwm_b); //delay(300); //OutputStop(); break; } default: break; } } } void OutputStop() { // digitalWrite(MOTOR_PWM_A, LOW); // digitalWrite(MOTOR_PWM_B, LOW); pwm_a = 0; pwm_b = 0; Timer1.pwm(MOTOR_PWM_A,0,0); Timer1.pwm(MOTOR_PWM_B,0,0); }
-----------------------
なんか、ソースが長いが、
まず、TimerOneのライブラリを落としてきて、登録しておく必要がある。いくつかバージョンがあるようだが、わたしはTimer_One_r11を使用した。(違いは知らない)
接続はcmdr_arduinoとまったく同じで、入力側に12VACアダプタを接続し、出力側をフィーダーにして線路につないでおく。線路には壊れても惜しくない車両(わたしだと鉄コレ15m級のモーター車。動きがとっても悪いから)。下は写真。ただしこの写真ではKato113系のモーター車だが。
操作は、ツール→シリアルモニタから行い
"]":モーター正転
"[":モーター逆転
"0":モーター非常停止(というか停止)
"2":PWMの周波数を半音上げる。
"1":PWMの周波数を半音下げる。
"4":PWMのDutyを+8する。(上限1023)
"3":PWMのDutyを-8する。(上限1023)
音の周波数については、ここを参考にした。表から、半音上げるためには周波数を1.05倍すればよいらしい。
これで動かしてわかったことは、モーターから音はするけど、回転しないとかがよくあり、パラメータが難しいかもと思ったことである。
適当な周波数に固定し、Dutyだけ変えるとE231系みたいな走り出し音になる。とかいっぱい遊んでしまって、時間が経ってしまった・・・。
音自体は、電車のモーター音のページ 様のところでいっぱい聞いてみたが、ちょっと奥が深くて、おなかがいっぱいになってしまった。何かに忠実なものにするのはやめようと心に誓った。
で、ここで、NゲージでVVVF音を出す動画を探そうと検索したら、しろくま 様の記事があり、動画をみて、「あ、こういうことしたい!」と横道にそれてしまうことにした。
<(3)VVVF音風なPWMコントローラを作る。>
で、ここまで作った後に、VVVFってそういえばどう音階が切り替わるんですか?と思い検索したら、使えそうなページを二つ見つけられた。
「1.車両用パワーデバイスのGTO、IGBT 3レベル回路とは 今後の素子は?」で、「3. IGBTと非同期PWM制御」のように周波数を変更してけば、本物っぽくなることがわかる。
で、具体的には、DCC(デジタル・コマンド・コントロール)館 様の記事の、周波数テーブルのようなものを作る必要があることがわかった。
そして、試行錯誤しながら、最終的に以下のスケッチを作った。
-------------------------------
#include <TimerOne.h> #define MOTOR_PWM_A 9 #define MOTOR_PWM_B 10 #define LED 13 //速度領域始め、終わり、周波数始め、終わり //速度は0-1023とする const int kasoku[5][4] = { { 0, 30,300,500}, { 31, 60,300,500}, { 61, 120,300,450}, { 121, 200,300,350}, { 201,300,300,320} }; const int loco_speed_max = 200; void OutputStop(); int duty_offset = 100; int pwm_duty = 32; double pwm_freq = 440; int pwm_a = 0;//pwm aは出力するか? int pwm_b = 0;//pwm bは出力するか? int loco_speed = 0;//現在速度 int loco_direction = 1;//方向 int loco_accel = 0;//加速係数 void setup () { //9,10は出力 pinMode (MOTOR_PWM_A, OUTPUT); pinMode (MOTOR_PWM_B, OUTPUT); //Serial使用 Serial.begin(115200); Serial.println("vvvf test K2"); //Timer1使用 Timer1.initialize(); //初期設定 int pwm_period = (double)1000000 / pwm_freq; Timer1.setPeriod(pwm_period); Timer1.setPwmDuty(MOTOR_PWM_A,pwm_duty * pwm_a); Timer1.setPwmDuty(MOTOR_PWM_B,pwm_duty * pwm_b); //PWMの開始 OutputStop(); } void loop () { if(Serial.available() > 0) { int ch = Serial.read(); //Serial.write(ch); switch (char(ch)) { case '2': { //DutyOffset+ duty_offset += 10; Serial.print("duty_offset+:"); Serial.println(duty_offset,DEC); break; } case '1': { //DutyOffset+ duty_offset -= 10; Serial.print("duty_offset-:"); Serial.println(duty_offset,DEC); break; } case '7': { Serial.println("Brake"); //loco_speed //loco_direction loco_accel = -1; break; } case '8': { Serial.println("ConstSpeed"); //loco_speed //loco_direction loco_accel = 0; break; } case '9': { Serial.println("accel"); //loco_speed //loco_direction loco_accel = 1; break; } case '0': { Serial.println("Emergency Stop"); OutputStop(); loco_speed = 0; //loco_direction = 1; loco_accel = 0; break; } case '[': { Serial.println("-direction"); //loco_speed = 0; loco_direction = -1; //loco_accel = 1; break; } case ']': { Serial.println("+direction"); //loco_speed = 0; loco_direction = 1; //loco_accel = 1; break; } default: break; } }//PWM制御関連 { loco_speed += loco_accel; //上限制限 if(loco_speed > loco_speed_max) { loco_speed = loco_speed_max; } if(loco_speed < 0) { loco_speed = 0; } Serial.print("loco_speed :"); Serial.println(loco_speed,DEC); if(loco_accel != 0) { for(int i = 0;i < 5;i++) { if ( (kasoku[i][0] <= loco_speed ) and ( loco_speed <= kasoku [i][1])) { //音色側 pwm_freq = (double)kasoku[i][2] + (double)(kasoku[i][3] - kasoku[i][2]) / (double)(kasoku [i][1] - kasoku [i][0]) * (double)(loco_speed - kasoku [i][0]); int pwm_period = (double)1000000 / pwm_freq; Timer1.setPeriod(pwm_period); Serial.print("Freq- :"); Serial.print(pwm_freq,DEC); Serial.print(" period :"); Serial.println(pwm_period,DEC); //電力側 pwm_duty = loco_speed + duty_offset; if (pwm_duty >1023) { pwm_duty = 1023; } if(loco_direction == 1) { pwm_a = 0; pwm_b = 1; } else { pwm_a = 1; pwm_b = 0; } //duty_offsetの設定によってはspeed0でも止まらないため if(loco_speed !=0) { Timer1.setPwmDuty(MOTOR_PWM_A,pwm_duty * pwm_a); Timer1.setPwmDuty(MOTOR_PWM_B,pwm_duty * pwm_b); } else { OutputStop(); } Serial.print("Duty(0-1023)- :"); Serial.println(pwm_duty,DEC); } } } } delay(80); } void OutputStop() { // digitalWrite(MOTOR_PWM_A, LOW); // digitalWrite(MOTOR_PWM_B, LOW); pwm_a = 0; pwm_b = 0; Timer1.pwm(MOTOR_PWM_A,0,0); Timer1.pwm(MOTOR_PWM_B,0,0); }
-------------------------------
システムは(2)と同じでcmdr-arduinoをds-dccデコーダーでやったときと配線は同じ。
操作は(2)同様 ツール→シリアルモニタで
"]":モーター車 正方向
"[":モーター車 逆方向
"0":モーター非常停止(というか停止)
"2":PWM Dutyのオフセットを+10する。(走り出しの調整)
"1":PWM Dutyのオフセットをー10する。(走り出しの調整)
"7":減速する。
"8":その速度で走る。
"9":加速する。
ソースだが、kasoku[5][4]が、音階を変えるLUTのようなもの。
5段持っており、例えば一段めは、”{ 0, 30,300,500},”なので、スピードが0~30のときに音階を300Hz~500Hzにかえるという意味。
loco_speed_max は車両の最高速リミット(本当は1023まで取れる)
スピードが上がるとモーター音が大きくなり、VVVF風の音はほとんど聞こえなくなる。
loopの最後にある”delay(80)”がLoopの周期をほぼ決めており、ここを大きくすれば加減速が遅くなる。小さくするともちろん加減速が速くなる。
以下が動画。音を大きくしないと聞こえないかも。
2015-05-17 00:45
コメント(2)
トラックバック(0)
PWMのキャリア周期を変更する理由について補足しておきます。
電車は見たとおりとても重たいので、始動には大電流を食います。しかし、IGBTやMOS-FETといった、電力調整半導体素子は、ON/OFFの周期が短い(キャリア周期が高い周波数である)場合、熱をたくさん出します。いわゆる、スイッチング損失と中の人達は呼びます。
なので、始動時・低速時は、ジージーなどとうるさいですが熱などの問題があるので仕方なく、キャリア周期の周波数を低くしてます。速度が高くなると、慣性で動けるので電流はそれほど食わないので、キャリア周期を高くしてうるさい音を聞こえない周波数(10kHzくらいになると人間の耳には聞こえない)にしてます。
メーカーによって、このキャリア周期と速度のバランスを変える味付けが違っていて、わざと周期をフラフラさせて分散させたり、速度との比例係数が違ったり、非同期から同期に切り替わるタイミングが違ったりします。
なので、ふじがやさんも、味付けをいろいろ工夫してみてください。
by Yaasan (2015-05-17 14:53)
コメントありがとうございます。
よく乗る電車はE231系なので、今度試行錯誤してみたいと思います。
和音は単純なPWMではないのでちょっと難しそうですが。
by fujigaya2 (2015-05-18 00:20)