GR-ROSE本「GR-ROSEでいこう!」出しました

中止となった技術書典8に代わってオンライン開催の技術書典 応援祭に「GR-ROSEでいこう!」を出しました。今回の応援祭では電子版のみですが、紙の本もただいま印刷中です。シリアルサーボ、ROS/ROS2、EtherCAT、Amazon FreeRTOSなど盛りだくさんの内容となっております。

私は2章「シリアルサーボを制御する」と7章「EtherCATでロボットの制御」を担当しました。

techbookfest.org

f:id:licheng:20200308214245j:plain

応援祭終了後の電子版および紙の本の販売については後日お知らせします。

2月のまとめ

進捗

  • GR-PEACHデバッグ環境構築
  • GR-MANGOの開発環境構築
  • GR-ROSEの同人誌の組版
  • SOEMのMbed LPC1768対応
  • SOEMのMbed GR-MANGO(β版)対応

動画公開


所感

年末以来準備を進めてきた技術書典8が中止になったのは残念でした。作った同人誌はオンライン開催の「技術書典 応援祭」に出す予定です。3月以降もイベント中止が相次ぐかんじですが、開発のほうはしっかり進めていきたい。

マイコンでEthernet

マイコンEthernetを使いたい場合、ハードウェアの構成に3つのパターンがある。いずれの場合もRJ45ジャックにはパルストランス内蔵のもの(MagJackなど)を使用するものとする。

マイコンEthernetMACを持たない場合

f:id:licheng:20200225211652p:plain

この場合、SPI接続等でEthernetMAC/PHYチップを外付けする。このような外付けMAC/PHYチップの代表的なものとして、WIZnet社のW5200やW5500がある。

例えば、Arduino UnoにEthernet Shield 2 (W5500搭載)を挿して使うような構成がこれに当たる。W5200やW5500の場合にはEthernetMACのみならずTCP/IPUDP/IPのプロトコルまで担ってくれるので、マイコン側の負荷が小さくてすむ。

マイコンの選択肢はほとんど自由であり、かなり低スペックのマイコンでも実現できる。ただし外付けチップがやや高価である。

マイコンEthernetMACを持つがPHYは持たない場合

f:id:licheng:20200225211724p:plain

この場合、EthernetのPHYチップを外付けする。マイコンとPHYとの接続にはMIIやRMIIという規格がある。このような外付けPHYチップの代表的なものとして、Microchip社のLAN8720A、LAN8742AやTI社のDP83848などがある。

EthernetMACを持つマイコンの場合、外付けPHYチップとRJ45コネクタを搭載したマイコンボードが市販されている場合も多い。例えばNucleo-F767ZI (STマイクロ STM32F767ZI) や GR-SAKURA (ルネサスRX63N)などである。mbed LPC1768 (NXP LPC1768)はPHYは搭載しているがRJ45コネクタは搭載していないので外付けとなる。PHYとRJ45コネクタはTX+,TX-,RX+,RX-の4線で接続する。

マイコンの選択肢はかなり絞られ、ハイエンド寄りとなる。また、プロトコルスタックのライブラリなりミドルウェアなりが提供されていることが必須となる。

マイコンEthernetMACもPHYも持つ場合

f:id:licheng:20200225211737p:plain

このパターンはかなり稀である。TIのMSP432E4やNXP(旧Freescale)のMPC5606E、MicrochipのPIC18F97J60などがあるが、筆者はどれも使ったことがない。回路構成はシンプルになるがなにぶん選択肢が少ない。

undefined reference to `typeinfo for クラス名'

C++で「undefined reference to `typeinfo for クラス名'」というよく意味の分からないエラーに遭遇した。

undefined reference to `typeinfo for BMI160Class'

どうも仮想関数(virtual が付いてる関数)を持つクラスで、仮想関数の最初の親クラスで実体が定義されていない場合に出るらしい。もうちょっとそれと分かるようなエラーメッセージにしてほしい。

make (e=87): パラメータが間違っています。

Mbedのプロジェクトをオンラインコンパイラからエクスポートして、WindowsEclipse系のオフラインビルド環境にインポートしてビルドすると次のようなエラーが出る場合がある。

make (e=87): パラメータが間違っています。

英語環境だと次の通り。

make (e=87): The parameter is incorrect.

このエラーはピルドのコマンドラインが長すぎる場合に発生する。WindowsのCreateProcess()でプロセスを生成する場合、コマンドラインの長さは最大32768文字であり、これを超える場合にこのエラーが出る。Mbed OS 5のプロジェクトで大量のオブジェクトファイルをリンクして実行バイナリを生成するときにこのエラーが発生することがある。実際、エラー発生時のリンカのコマンドラインを調べるとなんと5万文字を超えていた。

なお、LinuxMacでは発生しないようだ。LinuxMacのような「適切な」OSを使用する以外に回避策はないのかもしれない(笑)

参考

github.com

C#でゲームパッド入力

Windowsではゲームパッド入力はDirectXを介するのでキーボード入力ほど簡単には扱えない。C#アプリでゲームパッド入力を取るにはDirectXをラップしたなんらかの.NETライブラリが必要になる。このようなライブラリには有名なものとして SlimDX と SharpDX がある。SlimDX は2012年を最後にアップデートされておらず、DirectX 11までの対応となっている。SharpDX も残念ながら2019年に開発終了となってしまった。代替手段が見つかるまで当面は SharpDX を使うことにする。

NuGetでSharpDX.DirectInputをインストール

DirectInputDirectXの一部であり、マウス、キーボード、ゲームパッド等の入力を得るためのAPIである。SharpDX.DirectInputはこれをラップした.NETライブラリである。

「プロジェクト」→「NuGetパッケージの管理」で「DirectDX.DirectInput」を検索し、プロジェクトにインストールする。

また、C#ソースの冒頭に下記のusingを追加する。

using SharpDX;
using SharpDX.DirectInput;

変数の定義

// DirectInput
DirectInput dinput = new DirectInput();
// ゲームパッド
Joystick joystick = null;
// 使用するゲームパッドのID
Guid joystickGuid = Guid.Empty;

ゲームパッドを探す

ゲームパッドバイスまたはジョイスティックデバイスの中から最初に見つかったもののIDを取得する。

// ゲームパッドを探す
if (joystickGuid == Guid.Empty)
{
    foreach (DeviceInstance device in 
      dinput.GetDevices(DeviceType.Gamepad, DeviceEnumerationFlags.AllDevices))
    {
        joystickGuid = device.InstanceGuid;
        break;
    }
}
// ジョイスティックを探す
if (joystickGuid == Guid.Empty)
{
    foreach (DeviceInstance device in
      dinput.GetDevices(DeviceType.Joystick, DeviceEnumerationFlags.AllDevices))
    {
        joystickGuid = device.InstanceGuid;
        break;
    }
}

ゲームパッドの初期化

// 見つかった場合
if (joystickGuid != Guid.Empty)
{
    // ゲームパッドの取得
    joystick?.Dispose();
    joystick = new Joystick(dinput, joystickGuid);
    // ゲームパッドが取得できた場合
    if (joystick != null)
    {
        // バッファサイズを指定
        joystick.Properties.BufferSize = 128;

        // ジョイスティックの軸の最小値と最大値を -1000~+1000に設定
        foreach (DeviceObjectInstance deviceObject in joystick.GetObjects())
        {
            switch (deviceObject.ObjectId.Flags)
            {
                // 絶対軸または相対軸
                case DeviceObjectTypeFlags.Axis:
                case DeviceObjectTypeFlags.AbsoluteAxis:
                case DeviceObjectTypeFlags.RelativeAxis:
                    var ir = joystick.GetObjectPropertiesById(deviceObject.ObjectId);
                    if (ir != null)
                    {
                        try
                        {
                            ir.Range = new InputRange(-1000, 1000);
                        }
                        catch (Exception) { }
                    }
                    break;
            }
        }
    }
}

ゲームパッドの入力を取得

ゲームパッドの入力を取得する。

// ゲームパッドの入力を取得
try{
    joystick.Acquire();
    joystick.Poll();
}catch{
    // ゲームパッドが抜けた
    joystick?.Dispose();
    joystick = null;
    return;
}
// ゲームパッドのデータ取得
var jState = joystick.GetCurrentState();
// 取得できない場合
if (jState == null) { return; }
ボタン入力の判定

ボタン1,2,3...に対応する添え字が0,1,2...であることに注意する。
また、ボタン番号とボタンの対応はゲームパッド製品によってバラバラなのでなんらかのテーブル(配列変数)で対応付けを管理する。

// ボタン1が押された場合
if(jState.Buttons[0]){ }
十字ボタン入力の取得

十字ボタンはハットスイッチ(POV、視点の角度切替え)として扱われる場合と、X軸/Y軸として扱われる場合があり、製品によって異なる。

// POVの場合
if (jState.PointOfViewControllers[0] == 0){}     // 上
if (jState.PointOfViewControllers[0] == 9000){}  // 右
if (jState.PointOfViewControllers[0] == 18000){} // 下
if (jState.PointOfViewControllers[0] == 27000){} // 左
// X軸/Y軸の場合
if (jState.X < -500){} // 左
if (jState.X >  500){} // 右
if (jState.Y < -500){} // 上
if (jState.Y >  500){} // 下
アナログスティック入力の取得

一般的なゲームパッドにはアナログスティックが2本つまり4軸あるが、DirectInputが対応しているアナログ入力はX,Y,Z軸、X,Y,Z軸の回転、POVなどであり、現実のゲームパッドとうまく対応しない。軸の割り当てはこれまた製品によってまちまちである。下記に一例を示す。

// 左のアナログスティックはX軸とY軸
int x1 = jState.X;
int y1 = jState.Y;
// 右のアナログスティックはZ軸とZ軸の回転
int x2 = jState.RotationZ;
int y2 = jState.Z;

参考ページ

csdegame.net

LANコネクタの謎

基板取付用LANコネクタ(RJ45)は、LEDやシールドのピンを除くとどれも千鳥足の8ピンなのだが、ものによってピン配置がぜんぜん異なるので要注意。

パルストランスなしのもの

パルストランスを内蔵してないものの場合は、ケーブルの線が足に直結しており、ケーブルのピン番号と足のピン番号は一致する。

1 - TD+
2 - TD-
3 - RD+
4 - N.C.
5 - N.C.
6 - RD-
7 - N.C.
8 - N.C.

パルストランス内蔵のもの

1-3-2, 7-6-8型
1 - TD+
2 - TD-
3 - TCT (Vdd)
4 - N.C.
5 - CHS.GND
6 - RCT (Vdd)
7 - RD+
8 - RD-
1-4-2, 3-5-6型
1 - TD+
2 - TD-
3 - RD+
4 - TCT (Vdd)
5 - RCT (Vdd)
6 - RD-
7 - N.C.
8 - CHS.GND
1-2-3, 4-5-6型
1 - TD+
2 - TCT (Vdd)
3 - TD-
4 - RD+
5 - RCT (Vdd)
6 - RD-
7 - N.C.
8 - CHS.GND
不明なもの

どうやって使えばいいんでしょうか?
まあ、上記のいずれかだと仮定すればテスターで判別できるか?