So-net無料ブログ作成
  • ブログをはじめる
  • ログイン

R6n、IMA ADPCM Ver

 昨日は鉄道模型芸術祭2018に子供と行きました。ジオラマのレベルが高いです。街灯など光が大切だなと思いました。
 土曜日は最初、花見に行こうと思ったのですが、家の周りの桜はまだ咲き始めで満開は来週かなあと思ったのでやめたのですが池袋は満開でした。
re_DSC02243.jpg

 で、お決まりのコースで帰りがけに中野駅行きのバスに乗って、ホビーセンターカトーに寄って帰りました。アメリカ型のHO機関車がたくさんジャンク品で出ているとのことで、気に入ったものがあったら買おうと思ったのですが、ジャンク品の理由がどれも走行不良だったため、ぎくしゃくした走りや異音がするのを直すのは面倒だと思ったのと、あと数千円出せば新品のDD51とか手が届くので、買うのはやめました。
 数か月前に行ったときは、四季島がレイアウトを走っていましたが、今回は、特急「はと」青大将が走っていました。最後尾のマイテ58の3軸台車を見ながら、欲しいなあをちょっと思いましたが、見て満足したので買うのはやめておきました。
 で、本題ですが、R6nのSO103のIMA ADPCM化です。
 もともとSO103は8KHzのWAVでやっており、確か2秒ぐらい音が入ります。IMA ADPCM化する際に16KHzとかにすると、再生時間が長くならないので、8KHzのIMA ADPCMにしました。合計で4.8秒ほど再生可能です。しかし、SL103の16KHzソース→SO103の8kHz化でハマってしまい(nBlockAlignが16kHzと8kHzで値が違う)、半日かかってしまいました。
 で完成です。スケッチはこちらです。
動画はこんな感じで、一応4.8秒ぐらいは音が入れられると思います。音はF3~F6に割り当てています。


 スケッチと一緒に「ADPCM加工用8K」というフォルダを置いており、この中に、IMA ADPCM用の「WAV→ヘッダ変換」するソフトDSwav2c.exeも入れてあります。
 IMA ADPCMの作り方は、Audacityで
・モノラル8000Hz,で音を加工しておく。
・ファイル→オーディオの書き出しでファイル選択のダイアログを出す。
・ファイルの種類を「その他の非圧縮ファイル」を選ぶ。
・オプションボタンを押して、「非圧縮のオプションを設定してください.」のダイアログで、ヘッダ:WAV(Microsoft)、エンコーディング:IMA ADPCMを選んでOKボタンを押す。
・ファイル名(XXX.WAV)を決めて、保存する。
これでWAVファイルができます。例えばhorn.WAVとしておきます。
次にこれを DSwav2c.exeでヘッダファイルにして、ArudinoのIDEで読める形式にします。
・ソフトを開いてOpenボタンを押して、ファイルXXX.wavを選ぶ。
・Saveボタンを押して、ヘッダーファイルとしてセーブする。
例えばhorn.wavを選んで、horn.hを作ります。WAVファイル名で変数名が決め打ちされますので、お気を付けください。
そして、これをArudinoのスケッチから読めば、完成です。
・Arduinoのスケッチがあるフォルダにヘッダファイル(XXX.h)を置きます。
・次にスケッチを開いて、右上の▼ボタンからVVVF_sound.cppを選びます。
・12行目辺りからの音3~音6のヘッダファイル("XXX.h")を指定します。#include "XXX.h"の辺りです。
例えば音3はhorn.hを指定します。
・324行目辺りからの
return pgm_read_byte(&XXX_data[inOffset]);
の「XXX_data」を書き換えますhorn.hならhorn_dataとなります。音3~音6まで指定しておきます。
これで、コンパイルしてみて、容量が100%を超えなければ成功です。
また、それ以外のエラーは大体、ヘッダファイルの名前が間違っているとか、ヘッダ内の配列名とXXX_dataの名前が合っていないなどだと思います。
 音が途切れ途切れ鳴る場合や、音の高さ、再生スピードがおかしい場合は、sampling周波数を間違えていると思います。
 なお、このスケッチは吊り掛け音とかは鳴りません・・・。
 あとは、ディーゼルもやってみて一段落でしょうか?ディーゼル音は今16KHzなのですが、これを8kHzにして、警笛とブレーキ音入れて、が出来ればよいかなあと思っています。

コメント(4) 

DSAir人柱キット作成 [DCC/MM2シールド]

 「DSair 人柱様向け第一陣」が届きましたので、久しぶりに表面実装の電子工作をしました。
re_DSC02240.jpg

 Yaasan様の組み立て方の記事を見ながら、半田付け下手じゃないのか?と思っていましたが、今回のは特に抵抗が小さかったです。きれいに半田付けなんてできません。それでも、デコーダに比べて部品の実装密度が低いので、何とかルーペを駆使しながら完成しました。多分半田付けに1時間ぐらいはかかっています。
re_DSC02241.jpg

 FlashAirはヨドバシで買いましたが、16GBが4400円ぐらいで、32GBは4700円ぐらいだったので、貧乏性的にbit単価が安いということで32GBを買ってしまいました。32GBも使う予定が無いという残念な事実は置いておいて。
 で、FlashAirのアップデートはうまくできました。私のPCはLenovoのE420でElecomのカードリーダーを使用して行いました。(本体のSDカードスロットはWin10にバージョンアップした時に認識せず使えなくなってしまったため、USBによる外部接続です)
 なんか、Configファイル内の行数が少ない(MASTERCODEとかLOCKとかいう行が無かった)ことが気になりましたが、「APPAUTOTIME=3000000」を書き込んで、SD_WLANフォルダにHTML?をコピーしたら、ちゃんと動きました。
 なお、Yaasan様の記事にATMega328Pをアップデートしないといけないのかしなくてもよいのかわからない記事がありましたが、私はアップデートしないでも動きました。
 誰かも書いていましたが、緑色のステータスLEDがかなり明るいです。
 あとは表面実装の抵抗やコンデンサが取れそうなので、ケースは必須ですね。

 せっかくなので、動画です。ただし、今近くに車両がないので、SL103スケッチが入ったデコーダ開発ボードを無線で動かすというかなり残念な動画です。
 UIはスピードメーターの針をスライドさせてスピードを変えられるのが、とても素晴らしいと思います。



 
コメント(0) 

R6n用のSL汽笛付き [ds-DCCデコーダ]

 今日は雪で寒かったので、先週、いろいろな方から言われたMp3V5用というかR6n用のSLスケッチに汽笛を入れる件をやっていました。
 かなり苦労したのと、多分、また、割り込み的にぎりぎりだと思われるので、Nゲージでは京急のときのようなこと(集電でエラーが起きると、全体的に破たんする)があるかもです。
 やったことはSLスケッチの音部分を容量を無圧縮のWAVから半分ぐらいに圧縮できるIMA ADPCMに変更しています。16KHzのままですが、多分、今まで1秒分しか入らなかったものが、今回ので、2.1秒ぐらい入るようになりました。1秒分はSLのドラフト音4気筒分です。で、短い警笛が0.5秒、それよりも少し長い警笛が0.6秒といったところです。たぶん、8KHzにすると、もっと入るようにはなると思いますが、音も悪くなるので、やっていません。
 実験環境は写真の通り。
re_DSC02238.jpg

 DSBlueBoxで制御して、デコーダ開発ボードにアンプ386基板を付けて大きなスピーカーから音を出しています。
 使い方はF3が蒸気音のOnOff,F4,F5,F6が音のなるファンクションですが、F4,短い汽笛、F5少し長い汽笛を入れたら、もう容量がないです・・・。
 そして、スケッチはこちら。何となくなごでんさんの命名規則にのっとっていますが、もちろん非公認です。
 動画は以下です。何やっている動画かわかりづらいと思いますが、まあ、ドラフト音が鳴っているときに汽笛を鳴らすと、もちろんドラフト音は消えます・・・。が、重要なところでしょうか?


 あと、ホワイトノイズで蒸気音してみたらどうかという意見がありましたので、とりあえず、Audacityのジェネレーター→ホワイトノイズを使って、何となく作ってみました。ちょっと音が高いけど、SLにのせれば良さそうな音に聞こえそうですね。


 先週の運転会、情報交換会でへのへのもへじさんのNの蒸気が良い音を出していたので、私もAitendoから、エンクロージャ付きのスピーカーをいくつか買って見ました。
re_DSC02239.jpg

 まあ、今週はここら辺はいじらずに、DSAirを作っていく予定です。


コメント(6) 

S88 8ch 一応完成 [S88]

 金曜日は娘の小学校の卒業式でした。卒業証書授与で、200人近くいる児童が一人一人今までのことや将来の夢など一言しゃべるのですが、もちろんすごい時間になっていました。パイプ椅子でお尻が痛かったです。で、家に帰ってきてから、「自分の部屋が欲しい!」って言うじゃないですが。あなたに部屋をあげると私の遊び部屋がなくなってしまう!。どうしよう。
 さて、Elecrowからやっと基板とケースが届きました。
 メカ寸法は問題なしでした。
re_DSC02234.jpg

re_DSC02235.jpg

 自画自賛しますが、いい感じです。

 で、実動作させてみました。
 こんな実験環境です。
re_DSC02236.jpg
 
 re_DSC02237.jpg
 
 そうしたら、問題が出ました。
 6chの時は、Outputピン一つで6ch分の赤外線LEDを駆動させるためにトランジスタを二つ付けて、LEDの制限抵抗は220Ωを付け、電流は(5.0V-1.2V)/220 = 17mA流す感じで作っていました。つまり、LEDの駆動で最大17mA*6 = 102mA流れていました。
 今回は、省電力を狙って、8chのLEDを個別に制御しています。駆動にはArudinoのピンの駆動を使用しています。一応40mAまで可能とのことです。で、やってみたら、LEDが暗いようです・・・。かなり指を近づけないと光検出ができません。仕方ないので、LEDの制限抵抗を220Ω→100Ωに付け替えました。一応計算上は(5.0V-1.2V)/100 = 38mAですが、これで、6chのときと同じぐらいの検出になっているので、マイコンのIOピンの電圧がそれなりに低いことが予想されます。(測っていない)
 図面はこれです。
S88Control_8ch_K1.PNG

 もちろん、スケッチも個別点灯に直して、8ch対応しました。
 スケッチはこちらです。
 せっかく作ったので、デバッグも兼ねて久しぶりに自動運転でもやってみようかと思います。

 
コメント(0) 

やっと発送されたようだ [S88]

 2/23にElecrowに頼んだS88 8chの基板とアクリル板がやっと発送されたようです。
 あまりにも遅いので、なんか問題ありましたか?とメールしたら、春節明けでアクリル加工にすごい時間がかかっているとのことでした。
re_無題.jpg
 荷物のトラッキングで、日本には入ってきているようですので、木曜日ぐらいに受け取れるといいなあと思っております。
 AliExpressに注文したArduinoNano互換品は、集荷されてから全く動いていないようです・・・。
 
コメント(0) 

アクセサリデコーダのアドレスの説明 [ds-DCCデコーダ]

 昨日は子供と久しぶりに横浜の原鉄道模型博物館に行きました。子どもは大宮の鉄道博物館に行きたいと言っていましたが、今、リニューアル中のようですので。
 10:20に行ったら、11:00からの動鉄実習に参加できました。以前は混んでてなかなかできなかった印象があるのですが、空いてきたんでしょうね。ドイツのえんじ色とクリーム色の機関車を子供が動かしました。運転台はブレーキの圧力計の赤いメーターも動く本格的なものでした。
re_IMG_20180310_110006.jpg

 その後、ジオラマがを見ていると、少し前にへのへのもへじさんの記事で見かけたSBBクロコダイルが走っていて、電機機関車でロッドがあって、ボンネットがあるという個性的な姿そのままでした。これは確かに一家に一台欲しいかもと思いました。が、価格、高いですね。
 re_IMG_20180310_112949.jpg

 で、本題です、N様から、昇圧ポイントデコーダのスケッチについて質問があり、調べました。
 質問は、アクセサリのアドレスの話で、スケッチ(ino)では、

// This function is called whenever a normal DCC Turnout Packet is received
void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State)

命令でアクセサリのアドレスが来たらOn,Offしているけど、
このイベントを起こすnmraDCCライブラリ側を見ると、
BoardAddress =(((~pDccMsg->Data[1]) & 0b0111 0000) << 2) | (pDccMsg->Data[0] & 0b0011 1111 )
OutputAddress = pDccMsg->Data[1] & 0b0000 0111
OutputIndex = OutputAddress >> 1
Addr = (BoardAddress - 1) * 4 + OutputIndex +1

なので、BoardAddressに3とかが来ると、Addrは9~12とかになって、アドレスが一致しない気がするというお話でした。
 私はnmraDCCライブラリお任せで、中身を見ていませんでしたので、調べてみました。
 nmraDCCの仕様書、9.2.1の9ページ目に説明があります。
抜粋すると、
---
Basic Accessory Decoder Packet Format
The format for packets intended for Accessory Digital Decoders is:

{preamble} 0 10AAAAAA 0 1AAACDDD 0 EEEEEEEE 1

Accessory Digital Decoders can be designed to control momentary or constant-on devices, the duration of time each output is active being controlled by configuration variables CVs #515 through 518. Bit 3 of the second byte "C" is used to activate or deactivate the addressed device. (Note if the duration the device is intended to be on is less than or equal the set duration, no deactivation is necessary.) Since most devices are paired, the convention is that bit "0" of the second byte is used to distinguish between which of a pair of outputs the accessory decoder is activating or deactivating. Bits 1 and 2 of byte two are used to indicate which of 4 pairs of outputs the packet is controlling. The most significant bits of the 9-bit address are bits 4-6 of the second data byte. By convention these bits (bits 4-6 of the second data byte) are in ones complement.
---
で、アドレス部分は1データ目のAAAAAAと2データ目のAAAです。ボードアドレスへの変換は
BoardAddress =(((~pDccMsg->Data[1]) & 0b0111 0000) << 2) | (pDccMsg->Data[0] & 0b0011 1111 )
です。9桁しかありませんので、2の9乗で512しか取れません。
 しかし、DCCのアクセサリは2044まであります。どうしているかというと、今度は2データ目のDDDのうち1,2ビット目の取りうる値0~3を使います。これがサブアドレスみたいな感じです。
Dの0ビット目はいわゆるポイントの直進、分岐を制御するビットになります。
 桂庵様のページに説明が載っております。ここではアドレスとは言わずにF1~F4と表現されているようです。
 ここら辺をそのまま書くと、

アドレス ボードアドレス Dの2,1ビット
1 1 0 0
2 1 0 1
3 1 1 0
4 1 1 1
5 2 0 0
6 2 0 1

という感じです。ボードアドレスが1~511ぐらいで、各々のアドレスに対して4つのサブアドレスを持っているイメージです。コマンドステーションからは1~2044を送っているように見せかけて、内部でボードアドレスとサブアドレス(のようなもの)に分けて送っています。
 1データ目、2データのまんまの値がどうなっているかというと、Nuckyさんの日本式信号機でのちょっと複雑なアドレス表がそのままです。いままでNuckyさんのこの信号表、なんでこんな面倒なことになっているのかと思っておりましたが、謎が解けました。
 この表のとおりにして、2データ目の最下位ビットを直進か分岐かの値に書き換えてやることで、多分アクセサリデコーダのDCC信号になります。




コメント(0) 

自動運転士による自動運転計画(続くのか?) [自動運転]

 先週は「さよならの朝に約束の花をかざろう」を公開初日に見に行って、Yahooの映画評はかなりよいですが、映画に単純な楽しさを求める私としては、この映画どうかなあと思いました。で、今週も公開初日に「映画ドラえもん のび太の宝島」を子どもと見に行って、こっちは面白かったです。個人的には去年の南極モノよりも面白かったです。
 そして、ブレードランナー2049のブルーレイが出たので、買って見ました。映画館は3Dかつ字幕でかなり辛かったのですが、2Dで吹き替えだと目に負担がかからなくてよいです。

 さて、本題です。「自動運転士による自動運転計画(続くのか?)」です。
 RasPiにカメラが付き、もしかしたら画像処理も出来そうということで、将来的に画像処理も載せようと思い、今回は信号機の色を確認する処理をしてみます。もちろん、車載などという難しいことはまた今度考えるとして、WindowsPCとこのごろ流行りのPython+OpenCVでやろうと思います。
 まずは交通信号の画像処理について下調べです。ネットを探すと、
以下のものが上のほうに出てきました。
形状及び色特徴を用いた安全運転支援システムのための信号機検出処理
夜間動画像からの道路交通信号認識技術の研究
家庭用ビデオカメラ動画像からの道路交通信号認識


 で、調べるまでは、赤、黄、青の光っている信号機の画像を登録して、パターンマッチングして信号機を検出して、というのが一般的かなと考えていたのですが、どうも、こっちの業界はRGBをHSV変換してやるのが一般的らしいです。
・H:色相 赤から青
・S:彩度 色が淡い~濃い
・V:明度 明るさが暗い~明るい
 まあ、現在はもう一歩進んで機械学習なんでしょうが、私は今までグレースケールの画像処理しか扱っていなかったため、かなりお勉強になりました。
 そして、Python+OpenCVです。バージョン違いやC++の情報がごっちゃになっているなど、どうもネットで情報を探しにくかったですが、まあ、数日にらめっこしたらどうにか実験できるレベルにはなってきました。
 処理の流れは、(1)画像所得、(2)HSV変換、(3)赤信号、黄信号、青信号で二値化する(4)領域面積の一番大きい所の信号の色とする。です。LEDが明るすぎて、信号機の中心部分の色が白く飛ぶなど、どうも、カメラというのは人間の目に比べてダイナミックレンジの低さが気になります。
 実験環境は以下。DS BlueBoxNuckyさんの日本型信号機を使っています。
re_DSC02232.jpg

 画面はこんな感じです。
無題.png

 左上がUSBカメラ像。上真ん中が(使っていないが)Gray変換した二値化画像、左下が青信号二値化、下真ん中が黄色信号二値化、右下が赤信号二値化で、Pythonの環境はアナコンダとスパイダーとかいう環境でやっていますが、よくわかっていません。追加でOpenCVとNumpyとかいうライブラリ?を入れています。デバッグ中はエラーが出て途中で止まると、USBカメラのドライバごと落ちていきます。
 で、動画です。


 まあ、このままだととても外乱光に弱いので、LEDの強力な光を利用して、カメラにND
フィルターを付けてしまって、信号以外の風景や光は見えないようにしてしまおうかと思っていますが、そうすると、点灯する信号以外は全部無視の自動運転士が出来上がる・・・。なにかかなりショボく終わりそうな気がする・・・。

pythonのソースですが、以下です。(書きなぐりです。)
-----
#-*- coding:utf-8 -*-
import cv2
# import numpy as np
import numpy as np


def disp_text(frame,text = "",width = 0,height = 0,size = 1):
    cv2.putText(frame, text, (width, height), cv2.FONT_HERSHEY_PLAIN, size, (255, 255, 255), size, cv2.LINE_AA)

def disp_blue(hsv_frame,rgb_frame):
    # 色相、彩度、明度
    #lower_blue = np.array([70,50,100])
    #upper_blue = np.array([90,100,255])
    lower = np.array([70,50,200])
    upper = np.array([90,255,255])
    # HSV イメージから青い物体だけを取り出すための閾値
    mask = cv2.inRange(hsv_frame, lower, upper)
    # ビットごとのAND演算で元画像をマスク
    res = cv2.bitwise_and(rgb_frame,rgb_frame, mask= mask)
    cv2.imshow('blue',res)
    return np.sum(res)

def disp_yellow(hsv_frame,rgb_frame):
    # 色相、彩度、明度
    lower = np.array([15,50,200])
    upper = np.array([35,255,255])
    # HSV イメージから黄色い物体だけを取り出すための閾値
    mask = cv2.inRange(hsv_frame, lower, upper)
    # ビットごとのAND演算で元画像をマスク
    res = cv2.bitwise_and(rgb_frame,rgb_frame, mask= mask)
    cv2.imshow('yellow',res)
    return np.sum(res)

def disp_red(hsv_frame,rgb_frame):
    # 色相、彩度、明度
    #lower_blue = np.array([170,100,100])
    #upper_blue = np.array([190,255,255])
    lower = np.array([00,50,200])
    upper = np.array([10,255,255])
    # HSV イメージから赤い物体だけを取り出すための閾値
    mask1 = cv2.inRange(hsv_frame, lower, upper)

    lower = np.array([170,50,200])
    upper = np.array([180,255,255])
    mask2 = cv2.inRange(hsv_frame, lower, upper)
    
    mask = mask1 | mask2
    # ビットごとのAND演算で元画像をマスク
    res = cv2.bitwise_and(rgb_frame,rgb_frame, mask= mask)
    cv2.imshow('red',res)
    return np.sum(res)

def disp_gray(rgb_frame):
    gray = cv2.cvtColor(rgb_frame, cv2.COLOR_BGR2GRAY)
    # 二値変換
    thresh = 200
    max_pixel = 255
    ret, img_dst = cv2.threshold(gray,
                                 thresh,
                                 max_pixel,
                                 cv2.THRESH_BINARY)
    cv2.imshow('gray',img_dst)
    return np.sum(img_dst)

#def main():

# 動画の読み込み
cap = cv2.VideoCapture(1)

# 動画終了まで繰り返し
#while(cap.isOpened()):
while(1):
    # フレームを取得
    ret, img = cap.read()
    org_height, org_width = img.shape[:2]
    
    #画面表示を考えて大きさを小さくする
    size = (org_width//3*2, org_height//3*2)
    frame = cv2.resize(img, size)

    #HSV変換
        # BGR空間から HSV空間に変換
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    gray_s = disp_gray(frame)
    blue_s = disp_blue(hsv,frame)
    yellow_s = disp_yellow(hsv,frame)
    red_s = disp_red(hsv,frame)
    temp_str = "b,y,r"+str(blue_s)+","+str(yellow_s)+","+str(red_s)
    #どの色が強いか
    sig = np.array([blue_s, yellow_s,red_s])
    sig_name = np.array(["blue", "yellow","red"])
    sig_max = np.argmax(sig)

    _h, _s, _v = cv2.split(hsv)
    max_h = np.amax(_v)
  
    # フレームを表示
    # 左上から中央まで、太さ50の赤い四角形を描く
    # 画像の高さ、幅を取得
    height, width = frame.shape[:2]
    #cv2.rectangle(frame, (0, 0), (width/2, height/2), (0, 0, 255), 50)

    cv2.rectangle(frame, (0, 0), (width//2,height//3), (64, 64, 64), -1)
    temp_height = 20
    disp_text(frame,"v:"+str(max_h),0,temp_height)
    temp_height += 20
    disp_text(frame,temp_str,0,temp_height)
    temp_height += 40
    disp_text(frame,sig_name[sig_max],0,temp_height,2)
    cv2.imshow("raw", frame)
    
    #esc key
    k = cv2.waitKey(5) & 0xFF
    if k == 27:
        break

cap.release()
cv2.destroyAllWindows()


コメント(0)