2019年7月25日木曜日

四脚歩行ロボットを作る その2 サーボモータ着弾

意外とはやくサーボが届いたので喜び勇んで動作テスト。

1.SG90スペック

購入したサーボモータは小型低価格の定番らしいSG90です。

スペック及び外形寸法はこちら
重量:9g
ストールトルク:1.8kgf・cm(4.8V)
駆動方式:アナログ
ギア材質:POM
回転速度:0.12sec/60°(4.8V)
動作電圧:4.8V
動作角度:180°
PWMサイクル:20msec
制御パルス:0.5~2.4ms
A寸法:34.5mm
B寸法:22.8mm
C寸法:26.7mm
D寸法:12.6mm
E寸法:32.5mm
F寸法:16.0mm

最初はメーカーも知らず購入したのですが、ラベルにはTZTと書いてあります。メーカーサイトを探しても見つからず、ほぼ同じ外観のラベル違いの製品としてTower Pro SG90というものもあります。スペックも同じなのでおそらくどちらかのOEM生産品なのでしょう。
ちなみに私はAli Expressで購入して、一個108円(送料込!!)でした。もちろん国内販売もあり、みんな大好き秋月電子やAmazonにもあります。Amazonは中華業者の出店が多そうなので、Aliで買っても変わらない気はします。
似た製品で、デジタル制御タイプのSG90 Digitalや、360°回転タイプのSG90 360 Degree、金属ギアタイプのMG90S、そのデジタル制御のMG90D、360°回転タイプのMG90D Robot servo 360° Rotation高トルクタイプのSG92Rなどもあります。

今回はあくまでも制御の練習が目的ですので、Digitalと360は不要、この軽量低トルクで金属ギアは不要ですのでSG90アナログタイプで十分でしょう。
ただし、高トルクタイプのSG92Rは良い選択となる可能性があります。
バランスを崩してから転倒するまでの時間はロボットが小さい程短くなるため、アクチュエーターには素早さが必要とされます。SG90とSG92では、SG92の方が(カタログスペック上は)回転速度が高く設定されています。さらに、負荷時の動作速度はトルクが大きいほど無負荷時に近い速さを実現できるはずですので、高トルクモータの方が有利となるでしょう。

ちなみに重量当たりの出力を計算すると次の通りでした。

SG90:10W/g
SG92:17W/g

2.動作確認① 1モーターでの動作

初サーボモーターですのでとにかく動かしてみましょう。
試験環境は次の通りです。

マイコンボード:Arduino Pro Micro互換機
サーボ:SG90アナログ
サーボ電源:エネループ単四×4本直列(4.8V)



図中では電池2本ですが、実際にはエネループ×4本直列です。
PWM出力はPro Microの5番PINを使用しています。


動画の通り約180°の間を往復運動すれば成功です。
注意点としては、Pro Microの給電はPCからUSBケーブルで行っているのに対し、サーボモータはエネループの電池ボックスから給電している点、電池ボックスのマイナス側とPro MicroのGNDを繋ぐ点ぐらいでしょうか。

テスト動作時のスケッチは以下の通りです。
------------------------------------------------------------------------------
#include <Servo.h>

Servo myservo;

int pos = 0;
void setup() {
  myservo.attach(5);
}

void loop() {
  myservo.write(0);
  delay(500);
  Serial.println("1");
  myservo.write(180);
  delay(500);
}
----------------------------------------------------------------------------

3.動作確認② 複数モーターの同時制御

ロボット製作時には、12個のサーボを異なる速度で、異なる位相へ、同時に動かします。ここでは、脚一本分に相当する3個のサーボを別々に動作させるテストを行いました。
テスト用のスケッチはこちらのサイトを参考にしています。
サーボの一つはロータリーポテンショメータを使って回転速度を変更できる様にしてあります。
サーボ1は5番、サーボ2は6番、サーボ3は9番ピンから出力しています。
ポテンショメータはA0から入力しています。
こちらも電源はエネループ×4個、4.8Vです。


サーボ1(上段)は低速、サーボ2(中段)は低速、サーボ3(下段)はポテンショメータの入力に従い任意の速度で動きます。


テスト用スケッチは以下の通りです
--------------------------------------------------------------------------------------
#include <Servo.h>

Servo myservo1;
float angle1 = 0;
float vel1 = 0;

Servo myservo2;
float angle2 = 0;
float vel2 = 0;

Servo myservo3;
float angle3 = 0;
float vel3 = 0;

int sensorPin3 = A0; 
int sensorValue1 = 0;
int sensorValue2 = 1023;
int sensorValue3 = 0;

void setup() {
  Serial.begin(9600);
  myservo1.attach(5);
  myservo2.attach(6);
  myservo3.attach(9);
}

void loop() {
  vel1 = mapfloat(sensorValue1, 0, 1023, 0.005, 0.01);
  float n1 = (float) sin(angle1);
  float val1 = n1 * 90.0 + 90.0;
  myservo1.write(val1);
  angle1 += vel1 * 5;

  vel2 = mapfloat(sensorValue2, 0, 1023, 0.005, 0.01);
  float n2 = (float) sin(angle2);
  float val2 = n2 * 90.0 + 90.0;
  myservo2.write(val2);
  angle2 += vel2 * 5;

  sensorValue3 = analogRead(sensorPin3);
  vel3 = mapfloat(sensorValue3, 0, 1023, 0.005, 0.01);
  float n3 = (float) sin(angle3);
  float val3 = n3 * 90.0 + 90.0;
  myservo3.write(val3);
  angle3 += vel3 * 5;

  delay(5);
}

float mapfloat(float x, float in_min, float in_max, float out_min, float out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
--------------------------------------------------------------------------------

4.サーボ動作範囲の確認

SG90は安価なだけあり、動作バラツキがそれなりにある様です。テスト動作を行っていて気になったのは動作範囲のバラツキです。もっとも気になったのは角度指定で0~180°指定しても実際には180°の動作範囲が得られなかった事です。
気になったので、PWM制御のon時間を直接指定して動作させたところ、3つのサーボだけでも動作範囲が以下の様に異なっていました。

カタログ値 0°:500µsec 180°:2400sec
サーボ1  0°:406µsec 180°:2303sec 動作上限:2374sec
サーボ2  0°:544µsec 180°:2430sec 動作上限:2500sec
サーボ3  0°:479µsec 180°:2368sec 動作上限:2404sec

動作開始位置を0°としていますが、パルス幅はばらばら、可動範囲は180°+αとなっています。パルスの側の精度の可能性もありますが、オシロを持っていないので、確認はできていません。

ロボットに実装する際には、それぞれの可動範囲を測定し、0~180°に相当するパルス範囲を固体ごとに定義する必要がありそうです。





2019年7月24日水曜日

四脚歩行ロボットを作る その1.5 姿勢制御について悩み中

1.姿勢制御の構想中

さて、まだサーボモータがようやく届いたのですが、待ってる間に考えていたことなどを覚書。
4脚制御ロボットの制御方法についていろいろ悩み中です。2足歩行の文献はとても多いのですが、4脚歩行は少ない様です。本流ではないのでしょうか。

2足歩行の文献も全く手におえないものが多く、自分の数学能力の低さに気が遠くなります。
面白いので勉強しようとは思いますが、大学の先生たちと張り合う気にはなれないので、もう少し違う素人ならではアプローチをしたいものです。

2足歩行の重心移動や床反力や加速度云々のあたり、ぱらぱら眺めているだけでも面白い文献が多いのですが、これだけ計算やシミュレーションができるなら、ロボットにムーンウォークさせられるんじゃないでしょうか?
などと思っていたらさすがBoston Dynamicsさん、すでにやってましたね。四脚ですが。。

2.モノマネ開発法

専門的な知識の無い身としては、最新の理論や手法を実装するには超えられない壁がいくつもあります。以前にも述べたように、私が目指すはあくまでもモノマネです。生き物らしい動きをするロボットのモノマネ、まずはSpot miniの動画を観察してどんなところに生き物らしさを感じているのかを探しています。
また、どのようにその制御を実現しているのかを想像し、チープな方法へ置き換えていきたいと考えています。

Spot miniの動きを見てわかるのは、周囲の情報を幅広くセンシングして「理解」しているであろうこと。そしてその状況に対応する方法をいくつも想起して選択し実行しているであろうこと。
前進を始める際には、自分の姿勢を見て周りの障害物を見て、問題がなければ後肢を蹴りだし、前肢を前に繰り出しています。体が動くのは最後の結果であって、目で見て脳で考えるのが先にあるはずです。しかしそこには、高度なセンサーと高速な処理装置と高級な知能が必要で、私にはどれもありません。悩ましいところです。

ところで、どこで読んだか忘れてしまったのですが、人間の脳は意思よりも先に準備を開始しているという話を聞いた気がします。バットでボールを打とうと意図する前に脳の運動野の活動が始まっていると言う話だったかと思います。
脳は無意識の段階で環境へのフィードバックを始めているというのもありそうな話ですが、私はこんな風に妄想しています。
ボールを打つ前には、打つ準備を始めている脳とそうでない脳が量子論的に重ね合わさった状態にある。私は今、「ボールを打った」世界にいるので、観測されるのは「ボールを打つ準備を始めた脳」に収束した姿なのだ。
そう考えると、量子コンピュータが実用化されれば、ロボットの姿勢制御も飛躍的に前進するのかもしれませんね。

3.ロボット三重苦

はじめに神は天と地とを創造されました。これにより重力加速度が彼をとらえ、すきあらば転倒させようと手ぐすね引いて待ち受けています。また、神が「光あれ」と言われたにも関わらず、彼はそれを感知することができません。なぜなら彼には視細胞はおろか、フォトダイオードもCCDもCMOSも実装されていないからです。
彼は、視覚センサはおろか、音センサもスピーカーも持ちません。触覚や味覚や嗅覚も持っていません。三重苦どころの話じゃないですね。

しかし、かろうじてシリアル通信チャンネルは持っているので、音声によらない通信手段を持っています。下手すれば火田七瀬のテレパシー並みの能力です。
また、9軸ジャイロセンサと四肢の荷重を計測する圧力センサを実装したいと思っています。圧力センサの取付け位置は、形状的にも動体と脚の間に挟み込むくらいしかなさそうですが、調整は難しそうです。出来れば肉球センサみたいなものが開発されればうれしいのですが。
小鹿だって立つんだから、ウチのロボットだって立つくらいはできるんでしょう、タブン。目を閉じてたって、手探りで歩くくらいならできそうなので、高度なセンサーはオミットでよいでしょう。いわば目が開くまえに、三半規管と足裏の触覚だけで立ち上がろうとする生まれたての小鹿さんです。

あと、良いフィードバック回路を作るためには適切なフィルタリングが必要だろうなというあたりは想像しています。特に圧力センサの値なんてバラツキまくるだろうからかなりの補正が必要だと思われます。どうすればいいのかまだ想像もつきませんが、FFTで済むか、カルマンフィルタとか相補フィルタとかあれやこれやをごにょごにょする必要があるのでしょう。

4.集中制御よりも分散制御

想像しているだけでも、生き物らしく動かそうとすると、ボデーのスペック以上に脳のスペックが重要なことが判ってきました。しかし、数学的な理解も、コーディングの技術も、優れたハードウェアもないことはすでに述べた通り。どんなふうにゴマ化してモノマネに徹するかが、腕の見せ所になりそうです。

ボデーの重心位置を推定して理想的な四肢の軌跡を計算するのは大変なので、基本となる歩様に対して、加速度センサーの値から転倒防止のための補正を行う程度でなんとかバランスが取れないかと思っています。
そもそもArduinoの計算能力はそれほど高く無さそうですので、計算量を減らすうえでもこの方法がよさそうです。出来上がる動作は小鹿さんというより虫になりそうな気もしますが。
処理をするのはマイコンなのでハード的には集中制御ですが、ソフト的には脚一本ごとの動作修正を独立して判断させると言う意味で分散制御的な姿をイメージしています。

5.次はサーボの動作テスト

サーボモータを待ちがてらロボットの構造の検討などもしていたので、次回は構造概要についてまとめようかと思っていたのですが、目の前にサーボがあって素通り出来るほど大人では無いので、次回は動作テストなどを。


2019年7月23日火曜日

四脚歩行ロボットを作る その1 初号機構想

前回、ロボット製作に掛ける思いのたけを述べましたが、早速部品をポチりはじめました。順番が逆な気もしますが、初号機の構想の整理を進めたいと思います。

1.構想

  • 4足歩行。
  • いきなり出来るわけないので、初号機で何を勉強すれば良いのかを勉強する。
  • 歩けなくてもいいが、環境への対応が出来る事。環境からの入力をフィードバックさせる。
ざっくりしてますが今決まっているのはこれだけです。
これをもとに初号機の要件を具体化してみます。

2.とりあえずの仕様

  • 一足あたり3自由度
  • アクチュエータはサーボモータ
  • ジャイロセンサーで姿勢感知
  • 感圧センサーで一肢ごとの負荷検出
  • 制御は慣れてるArduino系ボードを使用
犬の歩行をイメージした場合、当然4本脚になるのですが、現実の肢の自由度は前と後ろで異なるようです。SPOT では足首以降の関節を省略しているので、前肢は肘に相当する部分に1自由度と肩に相当する部分に2自由度が与えられています。また、後肢についても前肢と同じ構造としていますが、実際の犬の関節構造を見ると、足首に相当する関節が大きな働きをしているため、ここに1自由度、膝に1自由度、股関節に2自由度なければならないことがわかります。(本当は前後肢ともに捻じれ方向にプラス1自由度ある)
生き物らしさを追求するうえでは後肢に4自由度を与えたいところですが、関節数が増えると制御が複雑になる点と、前肢と後肢を同じ構造にすることはハード面でもソフト面でも大幅なコスト減につながることを勘案し、初号機は前後肢同構造の3自由度とすることとしました。将来的には4自由度を狙いたいところです。

アクチュエータはまず一般的なサーボモータ駆動とします。初号機は一番安く入手性の高いものを選定します。

生き物らしさを追求するうえで欠かせないのは環境への適応だと思っています。歩行を含めた動作について素人ながらにいろいろと考察していますが、環境(外乱)に対するフィードバックはかなり「生き物らしい」動きを見せてくれるはずです。
4脚歩行ロボットの動画の中でも、Spotが蹴とばされても倒れないシーンを見て驚いた方は多いのではないかと思います。
転びそうになったところへとっさに肢を出して支えるこの動作はぜひ再現してみたい機能の一つです。
どうやって制御するかは後から考えるとしますが、体の姿勢や加速度を感知する6軸ジャイロは必須でしょう。また、アクチュエーターの負荷に応じて押し返すような動きが必須かと思いますので、フィードバック回路が必要でしょう。

制御は使い慣れている Arduino系ボードを使用する予定です。処理能力が足りなければ2号機以降で変更していくことでしょう。

3.部品選定

部品選定の強い味方、Aliexpress。安価な部品を適当に目についたものからカートに放り込みました。初号機なのでトルクが足りないとかセンサ感度が悪いとかそもそも使用出来ないとか、トラブル上等でいきます。失敗は成功のマザー。
サーボモーターは一番安いものを選んだだけです。生き物らしさを出すにはパワーウェイトレシオが不足していると思われますが、よしとします。

Pro Microを使っているのは前に買った在庫があるからです。小さいのが良ければNanoとか、PWM増設ボードが面倒ならMegaでもいいです。Pro MicroではPWM出力ピンが足りませんので、増設ボードとかマイコンを注文しました。使い方はまだ判りません。
センサー類は適当に選択。

サーボブラケットや構造体が決まってませんが、部品、特にサーボモーターの現物を見てから考えます。

4.まとめ

部品選定しましたが、初めて扱うデバイスばかりで出たとこ勝負感が否めません。ただ、社会人になって技術者続けてますが、ある程度構想したら予算をつけてプレ試作ぐらいしないと事業化の可否も判断出来ません。

現物を目の前にして改良を重ねる方が手っ取り早い事が多いですし、結果に重きを置かない趣味の世界に於いては、その過程こそが悦楽です。

次回はサーボモーターが届いてからになるでしょうか。
到着までにサーボモータの制御方法の予習をして待つことにします。

2019年7月20日土曜日

電子工作はじめたらロボット作りたくなった

1.時間が無くなる夢が広がる電子工作

フラシム用のコントローラを作りたくて始めた電子工作ですが、手段が目的化するというか、電子工作自体が楽しくなってしまいました。

使いもしないスイッチやボード類を買い漁ったり、仕様書眺めてどんなことが出来るのか調べてると夢が広がります。とてもすべてやる時間は無いので、やりたいことの方向性を整理してみるとおおよそ次の内容に集約されます。

  • 光りとか音とかアクチュエータとかちょこちょこ使ったインスタレーション的なな何か。
  • IoTでライフハックとかなんとか
  • VRなんかで使うマンマシンインターフェース
  • ロボット



2.どんなロボットが作りたい?

と言うわけで、ロボットにもいろいろジャンルがありますが、一番魅力を感じるのは「生き物と見分けのつかないロボット」です。



これまで見た中で一番「生き物らしい」ロボットと言えばこれです。動きの生々しさはもとより、困ったときにお友達呼んでくるとか、社会性まで想像させますよね。
すげーよBoston Dynamics。

こんなロボットうちにも欲しいし、出来るものなら作ってみたい。

3.生き物らしさとは?

どんなロボット(無生物)を作ろうかと悩むと、生き物や生き物らしさとは何か、という疑問にたどり着くのは逆説的で面白いですね。

私たちがロボットを見て「生き物らしい」と感じるポイントはどこにあるのでしょうか。宝塚の男役や歌舞伎の女形をみて男らしい、女らしいと感じる様に、無意識にパターニングして判別するアルゴリズムが我々の脳にあるのでしょう。

また、生き物らしさと言っても、動き、社会性、コミュニケーション、群れ、知能、自己保存、自己複製。。。いろんな側面が有ります。それぞれをテーマにしたいろんな映画や小説のワンシーンが頭をよぎります。

考え始めるとやりたいことや妄想がどんどん膨らむのですが、ロボットを通じて表現するならば、やはり「動き」の再現が第一のテーマです。ロボットをしこしこ作りながら、コードを書きながら、電気羊の夢について思いを馳せるのが第二のテーマでになるでしょう。

十数年前、2足歩行ロボットのブームがありました。おそらく手ごろなサーボモーターやマイコンなどが手に入りやすくなったという背景があったのでしょう。多くのキットが発売され、2足歩行ロボットが身近になりました。それまでの2足歩行の難しさを知っている人たちには感動を持って迎えられた反面、まだまだ生き物らしさの足りない動きに「この程度か」と思われてしまった部分もあった様に感じます。

ボストンダイナミクスのロボットたちを見ているとASIMOの時代よりもより強く「生き物らしさ」を感じます。センシングや計算能力、アルゴリズムの進歩により、ロボットも次の時代に入ったと感じます。そしてその生き物らしさは、今後ますます強く臭い立つようになるでしょう。

4.要件定義要望書

ロボットをつくった事がないどころか、基礎知識皆無、サーボモーターも触ったことが無いので要件定義など出来るはずもなく。。。
まずはただの要望書です。
  • 高望みしないが、嘘でもいいので生き物らしさを動きで表現したい。
  • Boston DynamicsのSPOT MINIみたいな4足歩行がしたい。
  • 高度な情報処理は出来ないので見た目だけでいい。人工無脳、あるいは中国語の部屋みたいなイメージで、外から見て生きていればいい。
  • ただし動きの出力だけでは生き物らしさはおそらく表現できないので、なんらかなのセンシングとフィードバックはいるだろう。
いまのところのざっくりしたイメージはこんな感じ。
どこまで出来るかわかりませんが、まずは初号機を作って何を勉強すれば良いのかを考えます。

2019年7月19日金曜日

P-51D武装関連コントローラ その5 回路設計とコーディング

DCS側の設定とProMicroのコードも出来たので、これに合わせて回路設計も行います。気をつけるのはなるべく小さく収めたいけど、はんだ付けが難しくない程度に。。。くらいです。

1.ユニバーサル基板の実装図

取扱いの利便性を考え、Arduino Pro Micro及び各スイッチ類への接続はQIコネクタを通して行います。よって基板上にはコネクタと分圧抵抗用回路用お抵抗しか載っていません。
表面
 各コネクタの接続先は次の通り。
・2,3,GNDのコネクタ
 TS3A(ON-OFF-ONトグル)
・4,5,GNDのコネクタ
 TS3B(ON-OFF-ONトグル)
・6,7,GNDのコネクタ
 TS3C(ON-OFF-ONトグル)
・8,GNDのコネクタ
 TS2A(ON-OFFトグル)
・9,GNDのコネクタ
 TS2B(ON-OFFトグル)
・10,Vcc.GNDの5PINコネクタ
 RS4A(4ポジションロータリスイッチ)
・16,GNDのコネクタ
 PBA(リセット用プッシュボタン)
・14,15,GNDのコネクタ
 RS3A(3ポジションロータリスイッチ)
・A0,A1,GNDのコネクタ
 RS3B(3ポジションロータリスイッチ)
裏面
・A2,Vcc,GNDのコネクタ
 VOLA(アナログボリューム)
・A3,Vcc,GNDのコネクタ
 VOLB(アナログボリューム)















デバイス名対応表

2.スイッチ類の配線処理

スイッチ類ヘはリード線をはんだ付けし、収縮チューブで保護。なんとなくフラットケーブル状になる様にして仕舞い易くしています。基板側にはQIコネクタのオスを加締め。2ピン、3ピン等のソケットを使い分けるとより便利。

3.コーディング

その3で動作確認したコードを組み合わせてコーディングします。関数化とかしてませんが、許してください。


#include "Joystick.h"

//シリアルモニタ出力 mon = 1 出力オン/ mon = 0 出力オフ
int mon = 0;

int P1 = 0;
int P2 = 0;
int i = 0;

//アナログ軸数値の初期化
int VOLAValue = 0;
int currentVOLAValue = 0;

int VOLBValue = 0;
int currentVOLBValue = 0;

//仮想ボタンの初期化
int TS3APosition = 0;
int currentTS3APosition = 0;
int PIN2State = 0;
int PIN3State = 0;

int TS3BPosition = 0;
int currentTS3BPosition = 0;
int PIN4State = 0;
int PIN5State = 0;

int TS3CPosition = 0;
int currentTS3CPosition = 0;
int PIN6State = 0;
int PIN7State = 0;

int TS2APosition = 0;
int currentTS2APosition = 0;
int PIN8State = 0;

int TS2BPosition = 0;
int currentTS2BPosition = 0;
int PIN9State = 0;

int RS3APosition = 0;
int currentRS3APosition = 0;
int PINA1State = 0;
int PINA0State = 0;

int RS3BPosition = 0;
int currentRS3BPosition =0;
int PIN15State = 0;
int PIN14State = 0;

int PBAState = 0;
int currentPBAState = 0;

int RS4APosition = 0;
int currentRS4APosition = 0;
int RS4AValue = 0;

//ジョイスティックの開始
Joystick_ Joystick;

void setup() {

//シリアルモニタ開始
  Serial.begin(9600);
  
//ジョイスティックの初期化
  Joystick.begin();

//デジタルピン設定
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);  
  pinMode(5, INPUT_PULLUP);  
  pinMode(6, INPUT_PULLUP);  
  pinMode(7, INPUT_PULLUP);  
  pinMode(8, INPUT_PULLUP);  
  pinMode(9, INPUT_PULLUP);
  pinMode(A1, INPUT_PULLUP);  
  pinMode(A2, INPUT_PULLUP);  
  pinMode(15, INPUT_PULLUP);  
  pinMode(14, INPUT_PULLUP);  
  pinMode(16, INPUT_PULLUP);  
}

void loop() {

//-----------------------------------------------------TS3Aの処理-------------------------------------------------------

//TS3Aの処理
  PIN2State = !digitalRead(2);
  PIN3State = !digitalRead(3);

  if(PIN2State == HIGH){
    currentTS3APosition = 0;
  } else if (PIN3State == HIGH){
    currentTS3APosition = 2;    
  } else {
    currentTS3APosition = 1;
  } 

  if ( currentTS3APosition != TS3APosition){
    for (i=0 ; i<=2 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentTS3APosition,HIGH);

   if (mon == 3){
      Serial.print("Position ");
      Serial.print(TS3APosition);
      Serial.print(" to ");
      Serial.println(currentTS3APosition);
    }

    TS3APosition = currentTS3APosition;
  }

//-----------------------------------------------------TS3Bの処理-------------------------------------------------------

//TS3Bの処理
  PIN4State = !digitalRead(4);
  PIN5State = !digitalRead(5);

  if(PIN4State == HIGH){
    currentTS3BPosition = 3;
  } else if (PIN5State == HIGH){
    currentTS3BPosition = 5;    
  } else {
    currentTS3BPosition = 4;
  } 

  if ( currentTS3BPosition != TS3BPosition){
    for (i=3 ; i<=5 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentTS3BPosition,HIGH);

   if (mon == 3){
      Serial.print("Position ");
      Serial.print(TS3BPosition);
      Serial.print(" to ");
      Serial.println(currentTS3BPosition);
    }

    TS3BPosition = currentTS3BPosition;
  }

//-----------------------------------------------------TS3Cの処理-------------------------------------------------------

//TS3Cの処理
  PIN6State = !digitalRead(6);
  PIN7State = !digitalRead(7);

  if(PIN6State == HIGH){
    currentTS3CPosition = 6;
  } else if (PIN7State == HIGH){
    currentTS3CPosition = 8;    
  } else {
    currentTS3CPosition = 7;
  } 

  if ( currentTS3CPosition != TS3CPosition){
    for (i=6 ; i<=8 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentTS3CPosition,HIGH);

   if (mon == 3){
      Serial.print("Position ");
      Serial.print(TS3CPosition);
      Serial.print(" to ");
      Serial.println(currentTS3CPosition);
    }

    TS3CPosition = currentTS3CPosition;
  }

//-----------------------------------------------------TS2Aの処理-------------------------------------------------------

//TS2Aの処理
  PIN8State = !digitalRead(8);

  if(PIN8State == HIGH){
    currentTS2APosition = 10;
  } else {
    currentTS2APosition = 9;
  } 

  if ( currentTS2APosition != TS2APosition){
    for (i=9 ; i<=10 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentTS2APosition,HIGH);

   if (mon == 3){
      Serial.print("Position ");
      Serial.print(TS2APosition);
      Serial.print(" to ");
      Serial.println(currentTS2APosition);
    }

    TS2APosition = currentTS2APosition;
  }

//-----------------------------------------------------TS2Bの処理-------------------------------------------------------

//TS2Bの処理
  PIN9State = !digitalRead(9);

  if(PIN9State == HIGH){
    currentTS2BPosition = 12;
  } else {
    currentTS2BPosition = 11;
  } 

  if ( currentTS2BPosition != TS2BPosition){
    for (i=11 ; i<=12 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentTS2BPosition,HIGH);

   if (mon == 3){
      Serial.print("Position ");
      Serial.print(TS2BPosition);
      Serial.print(" to ");
      Serial.println(currentTS2BPosition);
    }

    TS2BPosition = currentTS2BPosition;
  }

  
//-----------------------------------------------------VOLAの処理-------------------------------------------------------

//VOLAの処理
  currentVOLAValue = 1023 - analogRead(A3);
  if (currentVOLAValue < VOLAValue - 1 || currentVOLAValue > VOLAValue + 1){
    if (mon == 1){
      Serial.print("VOLA = ");
      Serial.println(currentVOLAValue);
    }
    Joystick.setXAxis(currentVOLAValue);
    VOLAValue = currentVOLAValue;
  }

//VOLBの処理
  currentVOLBValue = 1023 - analogRead(A2);
  currentVOLBValue = currentVOLBValue * 1.014;
  
  if (currentVOLBValue < VOLBValue - 1 || currentVOLBValue > VOLBValue + 1){
    if (mon == 1){
      Serial.print("VOLB = ");
      Serial.println(currentVOLBValue);
    }
    Joystick.setYAxis(currentVOLBValue);
    VOLBValue = currentVOLBValue;
  }

//-----------------------------------------------------RS3Aの処理-------------------------------------------------------

//RS3Aの処理
  PINA1State = !digitalRead(A1);
  PINA0State = !digitalRead(A0);

  if(PINA1State == HIGH){
    currentRS3APosition = 13;
  } else if (PINA0State == HIGH){
    currentRS3APosition = 15;    
  } else {
    currentRS3APosition = 14;
  } 

  if ( currentRS3APosition != RS3APosition){
    for (i=13 ; i<=15 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentRS3APosition,HIGH);

   if (mon == 2){
      Serial.print("Position ");
      Serial.print(RS3APosition);
      Serial.print(" to ");
      Serial.println(currentRS3APosition);
    }

    RS3APosition = currentRS3APosition;
  }


//-----------------------------------------------------RS3Bの処理-------------------------------------------------------

//RS3Bの処理
  PIN15State = !digitalRead(15);
  PIN14State = !digitalRead(14);

  if(PIN15State == HIGH){
    currentRS3BPosition = 16;
  } else if (PIN14State == HIGH){
    currentRS3BPosition = 18;    
  } else {
    currentRS3BPosition = 17;
  }

  if ( currentRS3BPosition != RS3BPosition){
    for (i=16 ; i<=18 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentRS3BPosition,HIGH);

   if (mon == 2){
      Serial.print("Position ");
      Serial.print(RS3BPosition);
      Serial.print(" to ");
      Serial.println(currentRS3BPosition);
    }

    RS3BPosition = currentRS3BPosition;
  }

//-----------------------------------------------------PBAの処理-------------------------------------------------------
  currentPBAState = digitalRead(16);
  if (currentPBAState != PBAState){
    if (currentPBAState == LOW){
//      Joystick.sendState();
      Serial.println("reset!");
    }
  } 
  PBAState = currentPBAState;  

//-----------------------------------------------------RS4Aのanalog処理-----------------------------------------------------

//anarog値読み取り
  RS4AValue = analogRead(10);

  if( RS4AValue >=0 && RS4AValue <= 80 ){
    P1 = 23;
  }
  if( RS4AValue > 160 && RS4AValue <= 330){
    P1 = 22;
  }
  if( RS4AValue > 400 && RS4AValue <= 590){
    P1 = 21;
  }
  if( RS4AValue > 650 && RS4AValue <= 850){
    P1 = 20;
  }

  if (P1 != P2) {
    P2 = P1;
    delay(50);
  } else {
    currentRS4APosition = P2;
  }

  if (currentRS4APosition != RS4APosition){
    for (i=20 ; i<=23 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentRS4APosition,HIGH);

    if (mon == 1){
      Serial.print("Position ");
      Serial.print(RS4APosition);
      Serial.print(" to ");
      Serial.println(currentRS4APosition);
    }

    RS4APosition = currentRS4APosition;
  }
  delay(50);
}


2019年7月17日水曜日

P-51D武装関連コントローラ その4 分圧抵抗回路でスイッチ数節約

前回予告した分圧抵抗回路の説明です。

分圧抵抗回路についてはこちらのサイトで詳しく解説されていますが、今回は簡易版の回路構成で十分ですので、少し改変して以下の通りの回路としました。

1.分圧抵抗回路によるスイッチング読み取り

分圧抵抗回路の考え方は、可変抵抗器とほぼ同じです。VccとGNDの間に任意の数の抵抗を配置し、それぞれの部位の電圧を図る事で、何個めのポジションにあるのかを推定します。回路図にすると次の通りです。
分圧抵抗回路
抵抗値の数を増やせばスイッチングのポジションも増やせます。
ここでスイッチが切り替わった際のアナログ入力の読込値を推定すると、このようになります。

  • スイッチが最上段の場合、1024×3/4=768
  • 上から2番目の場合、1024×2/4=512
  • 上から3段目の場合、1024×1/4=256
  • 上から4段目(最下段)、1024×0/4=0
すでにこの回路で実装しているのですが、、、良く考えたら一番上の抵抗いらなかったんじゃまいか!
まぁいいです。忘れましょう。

この様に、スイッチングによってアナログ入力値が段階的に変化するため、それぞれのポジションに対応したスイッチが押された事にしておくというのが分圧抵抗スイッチの考え方です。


2.分圧抵抗回路の限界

ただし、この方法には限界もあります。
一つ目は、ボタンの同時押しに対応出来ないと言う事。どれか一つなら簡単ですが、複数のボタンが押された場合、合成抵抗になってしまうのでどのボタンとどのボタンが押されたのかが識別できません。
今回は、ロータリースイッチへつなぐため、二つ以上のスイッチが同時にONにならないと言う条件がありますので問題ありません。

二つ目は、スイッチングの瞬間が不安定と言う事です。ロータリースイッチの特性上、スイッチが切り替わる瞬間、どこにもつながっていない状態、もしくは2ポジション同時につながっている場合などが発生します。今回は、切り替直後の不安定な時間帯の入力をソフト的にオミットしています。

三つ目はデジタル処理よりも時間がかかるらしいこと。
かかるらしいのですが、今回の用途では気にならないし、感じもしません。

3.分圧抵抗回路用のコーディング

このコードではPINナンバー10でアナログ値を読み取り、それぞれのポジションに応じてボタン20,21,22,23のON/OFFを送信しています。PC側のボタン認識は21,22,23,24です。

ポイントはポジション認識の為のアナログ値の閾値に一定の幅を持たせている点と、ポジションが変わった際にはdelayコマンドで50ms待たせて再確認している点です。スイッチ切り替え時のノイズはこれでほぼキャンセルされます。

#include "Joystick.h"
//シリアルモニタ出力 mon = 1 出力オン/ mon = 0 出力オフ
int mon = 0;

int RS4APosition = 0;
int currentRS4APosition = 0;
int RS4AValue = 0;
int P1 = 0;
int P2 = 0;
int i = 0;


//ジョイスティックの開始
Joystick_ Joystick;

void setup() {

//シリアルモニタ開始
  Serial.begin(9600);
  
//ジョイスティックの初期化

  Joystick.begin();

}


void loop() {
//anarog値読み取り
  RS4AValue = analogRead(10);

  if( RS4AValue >=0 && RS4AValue <= 80 ){
    P1 = 23;
  }
  if( RS4AValue > 160 && RS4AValue <= 330){
    P1 = 22;
  }
  if( RS4AValue > 400 && RS4AValue <= 590){
    P1 = 21;
  }
  if( RS4AValue > 650 && RS4AValue <= 850){
    P1 = 20;
  }

  if (P1 != P2) {
    P2 = P1;
    delay(50);
  } else {
    currentRS4APosition = P2;
  }

  if (currentRS4APosition != RS4APosition){
    for (i=20 ; i<=23 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentRS4APosition,HIGH);

    if (mon == 1){
      Serial.print("Position ");
      Serial.print(RS4APosition);
      Serial.print(" to ");
      Serial.println(currentRS4APosition);
    }

    RS4APosition = currentRS4APosition;
  }
  delay(50);
}


4.まとめ

Arduino Pro Microのスイッチ不足解消法は他にもいくつかあるのですが、今回はお試しで実装が一番楽と思われる方法を選択してみました。
思った通りの結果が得られたのでかなり満足です。

次回は全体のコーディングと基板実装を行い、いよいよ完成です。(もう完成してるけど)

2019年7月16日火曜日

P-51D武装関連コントローラ その3 ProMicro上での処理

P-51D武装関連コントローラの製作工程です。



先日出来てしまっているので後追い記事になりますが、完成品はこんな感じ。
DCS側の設定を理解したのでそれに合わせてコードを書きます。
ブログ上でコードを見やすくするツールもある様ですがまだ勉強出来てないので、勉強出来たら更新します。

1.ON-OFFトグルスイッチの処理

2ポジションのトグルスイッチは、ProMicroのピン数節約の為、すべてON-OFFタイプを使用し、arduino上でON-ONとして動作させます。PCとコントローラ上の動作の対比は次の通りです。

コントローラ上:PC上
トグルON   :トグルON側(ボタン10) ON
トグルOFF  :トグルOFF側(ボタン11) ON

※コード上のbuttonの名前は0~31、PC上の名前は1~32ですで一つずれています。

コードは以下の通りです。
---------------------------------------------------------------------------------------

//Device Toggle Switch 2position Aの制御

#include "Joystick.h"

//シリアルモニタ出力 mon = 1 出力オン/ mon = 0 出力オフ
int mon = 0;


int TS2APosition = 0; 

int currentTS2APosition = 0;
int PIN8State = 0;

//ジョイスティックの開始
Joystick_ Joystick;

void setup() {

//シリアルモニタ開始
  Serial.begin(9600);
  
//ジョイスティックの初期化
  Joystick.begin();

//デジタルピン設定 ピン8を使用
  pinMode(8, INPUT_PULLUP);
}

void loop() {

//TS2Aの処理
  PIN8State = !digitalRead(8);     //pin8の状態を読み取り

//トグル位置を更新
  if(PIN8State == HIGH){
    currentTS2APosition = 10;  
  } else {
    currentTS2APosition = 9;
  } 

//トグル位置をボタン10、11のON、OFF情報として送信
  if ( currentTS2APosition != TS2APosition){
    for (i=9 ; i<=10 ; i++){
      Joystick.setButton(i,LOW); 
    }
    Joystick.setButton(currentTS2APosition,HIGH);

   if (mon == 1){
      Serial.print("Position ");
      Serial.print(TS2APosition);
      Serial.print(" to ");
      Serial.println(currentTS2APosition);
    }

    TS2APosition = currentTS2APosition;
  }
}
---------------------------------------------------------------------------------------

2.ON-OFF-ONトグルスイッチの処理

3ポジションのトグルスイッチはON-OFF-ONを使用し、Aruduino上ではON-ON-ONとして信号を送信します。3ポジションのロータリスイッチも同様です。

コードは以下の通りです。
---------------------------------------------------------------------------------------
//Device Toggle Switch 3position Aの制御

#include "Joystick.h"

//シリアルモニタ出力 mon = 1 出力オン/ mon = 0 出力オフ
int mon = 0;

int TS3APosition = 0;
int currentTS3APosition = 0;
int PIN2State = 0;

int PIN3State = 0;

//ジョイスティックの開始
Joystick_ Joystick;

void setup() {

//シリアルモニタ開始
  Serial.begin(9600);
  
//ジョイスティックの初期化
  Joystick.begin();

//デジタルピン設定、ピン2、3を使用
  pinMode(2, INPUT_PULLUP);

  pinMode(3, INPUT_PULLUP);
}


void loop() {
//TS3Aの処理
  PIN2State = !digitalRead(2);
  PIN3State = !digitalRead(3);

  if(PIN2State == HIGH){
    currentTS3APosition = 0;
  } else if (PIN3State == HIGH){
    currentTS3APosition = 2;    
  } else {
    currentTS3APosition = 1;
  } 

  if ( currentTS3APosition != TS3APosition){
    for (i=0 ; i<=2 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentTS3APosition,HIGH);

   if (mon == 1){
      Serial.print("Position ");
      Serial.print(TS3APosition);
      Serial.print(" to ");
      Serial.println(currentTS3APosition);
    }

    TS3APosition = currentTS3APosition;

  }
}
---------------------------------------------------------------------------------------

3.アナログボリュームの処理

3軸トリムコントローラ「TRIM CUBE」でやったのと同じなのでテスト用コードのみ。


---------------------------------------------------------------------------------------
//Device VOLume Aの制御
#include "Joystick.h"

//シリアルモニタ出力 mon = 1 出力オン/ mon = 0 出力オフ
int mon = 0;

//アナログ軸数値の初期化
int VOLAValue = 0;
int currentVOLAValue = 0;

//ジョイスティックの開始
Joystick_ Joystick;

void setup() {

//シリアルモニタ開始
  Serial.begin(9600);
  
//ジョイスティックの初期化
  Joystick.begin();


void loop() {
//VOLAの処理
  currentVOLAValue = analogRead(A3);
  if (currentVOLAValue < VOLAValue - 1 || currentVOLAValue > VOLAValue + 1){
    if (mon == 1){
      Serial.print("VOLA = ");
      Serial.println(currentVOLAValue);
    }
    Joystick.setXAxis(currentVOLAValue);
    VOLAValue = currentVOLAValue;
  }
}

4.3ポジションロータリスイッチの処理

3ポジショントグルスイッチと同じなので割愛します。


5.4ポジションロータリスイッチの処理と分圧抵抗回路

分圧抵抗回路の詳細は次回、別に取り上げます。

6.リセットスイッチの処理

フライトコントローラの弱点の一つに、コントローラーの状態とフラシム内のスイッチの状態にずれが生じる場合がある。と言う点があります。リセットスイッチは、押した時点でのコントローラの状態を読み取り、フラシム内に反映させると言う機能です。

コントローラにはボタンを実装しているのでコードを書くだけなんですが、飽きてきた時間が無いのでまだ書いてませんmm
今回の仕様であれば、最初ボタン位置がずれていてもボタンを操作すれば一致するのであまり必要性は高くなかった。。。というのもあります。

7.まとめ

スイッチの動作確認がしやすいよう、ボタンの種類ごとに分けてアップしましたが、実際には全部まとめて一つのスケッチになっています。

2019年7月15日月曜日

P-51D武装関連コントローラ その2 DCS側の設定

コントローラを作る前に、DCS World側がどの様な操作を許しているのか確認し、必要が有れば修正します。

1.DCS Worldの設定ファイル

DCS Worldの入力設定を確認するにはまずこちらのファイルを参照します。

\DCS World\Mods\aircraft\P-51D\Input\P-51D\joystic\default.lua

このdefault.lua及びほかのいくつかのluaファイルを参照すると、制御方法のこまかな変更が出来るのですが長くなるのでまた改めて覚書的解説をアップします。
ちなみにluaって初めていじりましたがスクリプト言語だったんですね。iniファイルみたいな初期設定データかと思ってましたが、コードが書けるので改造の自由度が高そうです。

defaulut.luaの既存の設定で概ね問題無さそうなのですが、次の2項目は修正が必要でした。

  • Rockets (Delay/Inst)
  • Gunsight gyromotor power(ON/OFF)

この2項目は、トグルスイッチによって2ポジション切り替え行うデバイスですが、default.luaでは、一つのキーを押すたびに切り替わる動作となっています。

このまま実装しても良いのですが、トグルスイッチで動かしたいのでここを改造します。
この修正方法については某掲示板にて教えて頂きました。有難う御座いました。

2.default.luaの書き換え

今回は何も考えずにこの4行をdefault.luaに追記します。
追記場所は、

keyCommands = {

以下の行です。
判りやすい様、次の行あたりで良いでしょう。追記したら保存してください。

{down = 3015, cockpit_device_id  = devices.K14_GUNSIGHT, value_down = 1.0, name = _('Gunsight gyromotor power ON'), category = {_('K-14 gunsight')},
{down = 3015, cockpit_device_id  = devices.K14_GUNSIGHT, value_down = 0.0, name = _('Gunsight gyromotor power OFF'), category = {_('K-14 gunsight')},

{down = 3014, cockpit_device_id  = devices.WEAPON_CONTROL, value_down = 1.0, name = _('Rockets Delay'), category = _('Rocket Control Panel')},
{down = 3014, cockpit_device_id  = devices.WEAPON_CONTROL, value_down = 0.0, name = _('Rockets Inst'), category = _('Rocket Control Panel')},

※注意
1.luaファイルを編集する際には、編集前のファイルをバックアップしておきましょう。
2.ファイルを保存する際は、文字コードをANSIにしましょう。
私はメモ帳で編集後、自動的にUTF-8でされてしまい、DCSに正しく読み込まれないというトラブルに合いました。文字コードが原因とは全く気付かず、こちらも某掲示板の優しい先人に相談してようやく解決しました。アドバイスくれた方、本当にありがとうございますmm

3.キーバインド設定変更

これはコントローラ完成後の作業でよいのですが、2項の修正により
「K-14 gunsight」のカテゴリに、
  • Gunsight gyromotor power ON
  • Gunsight gyromotor power OFF
「WEPON CONTROL」のカテゴリに、
  • Rockets Deley
  • Rockets Inst
の項目が現れていますので、コントローラのトグルスイッチに割り当てられます。

4.まとめ

DCS側の設定が出来たので、実際のスイッチ類の動作とDCSの設定に矛盾しない信号を送らなくてはいけません。次回はスイッチごとの処理を反映するArduino Pro Microのコードを考えます。



2019年7月13日土曜日

P-51D武装関連コントローラ その1 作りたいもの

前回は3軸トリムコントローラを製作するなかで、Pro Microの使い方の習熟を行ったので、次にストレス作業となっている武装制御系のコントローラを製作します。

1.要件

  • DCS WorldのP-51D用を想定。
  • K-14ガンサイトとか、ロケット、爆弾系の操作をコントローラ化する。
  • 前回同様、筐体関連の工作は手抜き仕様で。
  • スイッチ類の形状や配置は雰囲気仕様で。
P-51Dコックピット

2.コントローラ化するスイッチ類

  • Gun safty switch(GUN CAMERA SIGHT/OFF/CAMERA SIGHT)
  • Bomb arming switch(left ARM/left OFF/ left CHEM RELEASE)
  • Bomb arming switch(right ARM/right OFF/ right CHEM RELEASE)
  • Bomb-rocket selector switch(SAFE/TRAIN/BOTH/ROCKET)
  • Rockets (Delay/Inst)
  • Gunsight target span(Increase/Decrease)
  • Gunsight mode(FIXED/FIXED & GYRO/GYRO)
  • Gunsight gyromotor power(ON/OFF)
  • Gunsight brightness(Increase/Decrease)
  • reset ※USBデバイスの状態とソフト上のデバイスのポジションを合わせる。
Gun、Bomb、Rocket 周りの制御盤

K-14 Gunsightの制御スイッチ

3.チャンネル節約について

これらの制御を行うと、デジタル24チャンネル、アナログ2チャンネル、合計26チャンネル必要です。ProMicroでは最大16ピンしか入力に利用できないので、ピン節約をしなければなりません。

今回のデバイスはトグルスイッチやロータリースイッチを多用しますので、複数のスイッチ状態が自動的に決まるケースが有ります。たとえば、Gunsight gyromotor powerがONの時、OFF側の回路は必ず切れています。この様な条件を使用すると、信号のチャンネル数はデジタル16チャンネルまで減らせます。
しかしアナログ2チャンネルがありますのでデジタルに使えるのは14チャンネルまで。まだ足りません。

そこで4ポジションのロータリースイッチについてはアナログチャンネルを1つ使って分圧抵抗回路を組み、ソフト上で4チャンネルのデジタル入力に変換します。これにより、アナログチャンネルが+1、デジタルチャンネルが-3となり合計16チャンネルとなりました。

DCS上のデバイス名や操作内容と、コントローラー上の対比です。


4.使用する部品

主にコントローラ上に実装するスイッチ類
  • ON-OFF   トグルスイッチ×2
  • ON-OFF-ON トグルスイッチ×3
  • 3ポジション ロータリースイッチ×2
  • 4ポジション ロータリースイッチ×1
  • B10K    ロータリーポテンショメーター×2
  • モメンタリプッシュボタン ×1
回路構成に必要なもの
  • Pro Micro ×1
  • 20穴×30穴くらいのユニバーサル基板 ×1 
  • QI規格コネクタ類適量
  • 信号用ケーブル類定量
  • ピンソケット適量
  • 10kΩ抵抗 ×4
筐体構成に必要なもの
  • セリアで買った木製小物入れ ×1
  • セリアで買った水性ニス、マホガニー色 ×1
  • 小物入れにフタできるサイズの2tくらいのプラ板 ×1
  • φ2×6㎜くらいの木ねじ ×4
  • micro USBケーブル 2M
  • 透明地、白文字のテプラ
消耗品を除くと必要なものはこれくらいだったかな。
スイッチ類はメーカー等問わないけど、基板取付けタイプでは無くて筐体へのねじ止めタイプを選びましょう。
トグルスイッチやプッシュボタンには、動作によってモメンタリタイプとオルタネートタイプが有ります。モメンタリは押している間だけON(OFF)になって離すと原点に戻りOFF(ON)になります。オルタネートは一度ONにするともう一度戻さないとOFFにならないタイプです。今回のトグルスイッチはすべてオルタネート、プッシュボタンはモメンタリです。

5.まとめ

作りたいものの姿が見えてきました。
次回は構想通りの制御が可能か、DCS側の仕様を確認します。

2019年7月12日金曜日

3軸トリムコントローラ製作 その4 コーディングと命名

今回製作した3軸トリムコントローラですが、「TRIM CUBE」と命名しました。だれか商標登録してください。

大した内容ではありませんが、最後にArduino Pro Micro用のコードもアップしておきます。アナログ入力の回路自体はとても簡単なので、ポテンショメータの実装はこちらこちらなどを参考にしてみてください。

部品実装が面倒だったので基板面積削減のため、チャタリング防止はコード上でごく簡単に処理しています。実用上は問題無し、至極快適です。

以下コードです。
------------------------------------------------------------------------------------------
//trim cube v2.00
                                                                                     
#include "Joystick.h"
int xAxis = 512;
int yAxis = 512;
int zAxis = 512;
int currentXAxis = 512;
int currentYAxis = 512;
int currentZAxis = 512;
int dev = 0;
int dev2 = 0;
int mon = 0; // mon = 1のとき、シリアルモニタ出力
//ジョイスティックの開始
Joystick_ Joystick;
void setup() {
//シリアルモニタ開始
  Serial.begin(9600);

//ジョイスティックの初期化
  Joystick.begin();
}
void loop() {
//X軸読み取り
  xAxis = analogRead(A1);
  dev = xAxis - currentXAxis;
  dev2 = dev * dev;
//チャタリング防止のため、従来値と現在値の差が2以上の場合、データ更新
  if (dev2 > 2){
//ジョイスティック出力
    Joystick.setXAxis(xAxis);
//従来値を更新
    currentXAxis = xAxis;
    if (mon == 1){
      Serial.print("x = ");
      Serial.println(xAxis);
    }
  }
//Y軸読み取り
  yAxis = analogRead(A2);
  dev = yAxis - currentYAxis;
  dev2 = dev * dev;
//チャタリング防止のため、従来値と現在値の差が2以上の場合、データ更新
  if (dev2 > 2){
//ジョイスティック出力
    Joystick.setYAxis(yAxis);
//従来値を更新
    currentYAxis = yAxis;
    if (mon == 1){
      Serial.print("y = ");
      Serial.println(yAxis);
    }
  }
//Z軸読み取り
  zAxis = analogRead(A3);
  dev = zAxis - currentZAxis;
  dev2 = dev * dev;
//チャタリング防止のため、従来値と現在値の差が2以上の場合、データ更新
  if (dev2 > 2){
//ジョイスティック出力
    Joystick.setZAxis(zAxis);
 
//従来値を更新
    currentZAxis = zAxis;
    if (mon == 1){
      Serial.print("z = ");
      Serial.println(zAxis);
    }
  }
}



2019年7月11日木曜日

Track IR 5 無線化

正確にはClip Pro無線化かな?

フラシムのヘッドトラッキングにTrack IR5+Clip Proを使ってるんですが、ヘッドホンの線が右側、Clip Proの線が左側と言う、ストレスフルな環境でした。

ヘッドホン自体が音楽観賞用のヘッドホンを使ってたこともあり、無線ヘッドセットに買い替えようと決意。せっかく無線にするんだからClip Proも合わせて無線化しました。

注意事項
著者は電子工作初心者です。試行錯誤の過程を楽しんでいただくためのブログです。実際に試したデバイスだけを載せていますが、回路が間違っている可能性や安全対策が不十分な場合があります。たぶん、おそらく、確実にあります。電子工作に関してはご自身でも再考し、ご自身の責任の範囲内で実施して下さい。願わくば、間違いなどは優しく指摘して頂くとありがたいです。

1.設計要件

  • 赤外線LED3個を発光させる。
  • 電源はわが家の標準電源(エネループ)を使用する。
  • ヘッドセットへの着脱はなるべく簡単に。
  • 構造もなるべく簡単に、なるべく軽く。

2.設計のポイント

①赤外線LEDの選定

 先人の知恵はありがたく頂きます。
 OSI5FU5111C-40 ×3個
 順電圧Vf=1.35V、max1.6V
 許容損失Pd=160mW 

実はこの情報を知る前に適当に買った赤外LEDを使ったのですが、光が拡散しすぎていたらしく正確に位置判定してくれませんでした。OSI5FU5111C-40の仕様書を見ると50%Power Angleが15°となっています。たぶん光の50%以上が前方15°以内に収束してるんでしょう。ちがったらごめん。
 

②電源の選定と回路設計
LEDが決まったので電源と回路を考えます。エネループを使う事は確定なので、回路も設計可能です。軽量化を考慮してエネループは単4を使用します。エネループの電圧はおよそ1.2Vですので一本では電圧不足、電池は2本直列でつなぎ、LEDは3個並列接続します。抵抗を使って電圧を2.4Vから1.35V付近まで落として使用するものとします。

回路図にするとこんなかんじか?


 抵抗で落としたい電圧=2.4V-1.35V=1.05V
 LEDに流す電流=100mA×3=300mA
 抵抗の容量=R=V/I=1.05V/0.3mA=3.5Ω
 実際には3.3Ωの抵抗を入れるとすると、抵抗での電圧降下=3.3Ω×0.3mA=0.99V
 LEDに掛かる電圧=2.4V-0.99V=1.41V

ちょいと電圧高めですが、最大値の1.6Vは下回ってるのでいいのか?

※ちなみにこのデバイスはひと月ほど前に完成しているのですが、10Ωの抵抗を2個並列で入れた気がする・・・。その場合、合成抵抗は5Ωで、電圧降下は1.5V、LEDへの印加電圧は0.9Vになって光らないよな。。。。使えてはいるけど何かおかしい気がするので現物を要再確認ですね。

気になる連続使用時間についてですが、単四エネループの電池容量は一本当たり750mAh、二本で1500mAh。回路の電流は300mAなので、100%使用時5時間、実際には5掛けか7掛けとして2.5~3.5時間は持つんじゃないでしょうか。一日での使用時間が長くて2~3時間なののでまずは良しとしておきます。必要なら大容量の電池に改造しようかな。

③電池ケース
単四エネループが2本入るケースならなんでもいいんですが、スイッチ、フタ付きをみつけたのでこれにしました。


④構造体
 一番頭を悩ませたのはこれです。どうやってclip proの形状をつくろうか。3Dプリンタ案、板金加工案、プラ材加工案、金属パイプ加工案など有りましたが、最終的には100円ショップのアルミハリガネで作りました。
 たしかφ2程度のものを使用しています。手で簡単に曲がりますので形は自在に変形可能。分岐形状が有りますがトポロジー的には?十字架と同様の形状です。クロス部を細いピアノ線でぐるぐる巻きにし、エポキシ接着剤で固定しています。電池ボックスとアルミフレームもエポキシ接着剤で固定しました。
ダイソーのワイヤー

ダイソーのエポキシ接着剤

⑤着脱機構
 ヘッドセットと無線クリップの着脱には、3Mのデュアルロックファスナーと言う面ファスナーを使いました。オスメスのないマジックテープの様なもので、マジックテープよりも強い接合力を持ち、かつ繰り返し着脱も可能です。
 たまたま仕事の関係で3Mの方からサンプルを頂いたのですが、今回の用途にはかなり適しています。正規品のclip proも着脱のヒンジ構造が弱点だったりしますが、面ファスナーなら可動部分がなく破損の心配がありません。
 ただし、ヘッドセットやクリップへの取付けは両面テープですのでこれが嫌な方は別の方法を考える必要があります。

3.完成品


なんとか形になりました。配線やLEDは収縮チューブで固定していますので、見た目は悪いけど形はすっきりまとまりました。



ヘッドホンへは面ファスナーを使った固定ですが、しっかり食いついてます。

4.今後の改良点
ひと月ほど使ってみて、改良したいポイントもいくつか見つかっています。モチベーションが上がったらまた改良してみようかな。
  • アルミワイヤーが柔らかすぎて変形しやすい。
  • 電池のもちはもっと伸ばしたい。
  • 電源の消し忘れ防止をしたい。
  • 結局ワイヤレスヘッドホンはまだ買ってないので無線化になってない。。。
アルミワイヤーでも十分機能してはいますが、置き方によってはヘッドホンの自重程度で変形します、次作るなら番線とかで作っても良いかも。
電池のもちは、単3にするだけで寿命2.5倍、単3のエネループプロを使えば3.3倍になるので必要なら改造しても良いかな。
赤外線LEDは目視では点灯が判らないので、ついつい消し忘れてしまいます。インジケータ用のLED追加すると使用時間が短くなるので悩ましい。
ワイヤレスヘッドホンはlogicoolのG933買おうかと思ってたら最新型のG933Sが発売されててまさかの2万円オーバー。ちょっと躊躇してます。

完全無線化までは進んでいないものの、ケーブルが2本から1本になったたでけでストレスは大幅に減少しました。必要な工作スキルは低く、費用もかなり安いのでお勧めの一品です。



2019年7月10日水曜日

3軸トリムコントローラ製作 その3

3軸トリムコントローラの、製作ポイントは次の通り。

①木製筐体で手抜き工事

当初、自作金属版とか、市販の金属ケース、プラケースを使おうかと考えていたのですが、加工の手間とコストを考えて100円ショップの木製小物入れにしました。

凝りに凝ったDIYもやってみたいのですが、私の様な初心者は時間も費用も技術もツールも不足していますのでハードルは可能な限り下げる方が吉ですね。

製作ペースを出来る限り上げて出来たものをさらに改良すると言うスキームを回したいと思います。

②コントローラはarduino pro micro 互換機

正確にはSparkfun pro microの互換機です。国内でも販売してますが、Aliexpressで300円強で送料無料って、すごいですよね。ちなみに私はココで5個ほど買って在庫してますが、今のところ不良品ではなさそうです。

pro microの特徴はATmega32U4というマイコンを使っていて、簡単にUSBデバイスを製作出来る事(だそう)です。実際私にもできました。


pro micro のI/O接続仕様



③制御用のライブラリはjoystick.h

pro microをUSBコントローラとして使用する際には、既存のライブラリを利用するのが簡単です。

フライトコントローラを製作する際は、joystick.hを利用すればやりたいことはほぼ出来るのではないでしょうか。


④ロータリーポテンショメータはセンタークリック付き

ロータリーポテンショメータとはいわゆるボリュームダイヤルです。ぐりぐり回すと抵抗値が変わって入力値を変化させます。今回はトリム制御を行うため、通常はセンター位置ですので、センター位置でクリック感が欲しい。イメージはコンポなどについている左右スピーカのバランスをコントロールするダイヤルです。

ネットで探したのですが、いまいちヒットしないので、出張で東京に行った際、秋葉原に行ってお店で聞いて買いました。型番が良くわからないですが、おそらくアルプス電気製ですね。

クリック有無に関係なく、ポテンショメータを使う際は、B10K程度が好ましい様です。電圧が5V、抵抗が10KΩの際、電流は0.5mAとなります。arduinoの許容電流は20mAなので十分マージンが取れていますね。


3軸トリムコントローラ製作 その2

フライトコントローラ製作と相成った次第ですが、ざっくりした要件は次の通りです。

設計要件

  • DCS world P-51Dでの使用を想定。
  • 出来ればレシプロ機全般で使えるといいな。
  • 筐体の加工は簡単に。

今やってるフラシムは、DCS world、War Thunder、IL-2 BoSですが、前回もお話しした通り、DCS worldのクリッカブルコクピットをより快適に使いたいと言うのが発端です。

P-51Dやレシプロ機にこだわっているのはあくまでも趣味ですが、現用機はスイッチ多すぎてやってられないってのもあります。

でもって、いきなり複雑なものを作るのもハードルが高いので、まずは3軸トリムコントローラーを製作します(しました)。

部品、材料

  • arduino pro micro互換機×1
  • センタークリック付きロータリーポテンショメータB10K×3
  • コントローラノブ
  • USB microケーブル 2m×1
  • セリアで買った木製小物入れ H100くらい×W65×D65 ×1
  • 適当なプラ板 2㎜厚くらい×65×65 ×1
  • φ2×8㎜くらいの木ねじ ×4
  • AWG23くらいの信号線とか半田とかの消耗品


でもって

これが

こうなって

こうです。


YAW、PITCH、ROLLの3軸に対応したアナログ入力を行うデバイスです。
センタークリック付きのボリュームダイヤルを使っていますので、通常はセンター位置、必要に応じて左右へ調整出来ます。

機体によってトリム軸の有無は異なりますが、P-51Dは3軸すべてのトリム操作が可能です。うん、快適!

ESP32でスマホとシリアル通信 その2 BluetoothでLチカ

1.BluetoothでLチカ制御 前回、Bluetoothでの双方向通信に成功しましたので、ついでに少しスケッチを修正してスマホからESP32のLチカを制御した様子が以下の動画です。 撮影が悪くて画面が良く見えませんが、"1"を送信...