PNGやJPEGの解像度(DPI)の変更

ふだんはあまり気にしないことだが、PNGファイルやJPEGファイルには物理的な解像度の設定値を持っている。PCの画面表示に比べて印刷では高い解像度の画像が求められる。言うまでもなく、同じピクセル数の画像でも高解像度の設定のほうが物理的な寸法は小さくなりピクセルの密度が高くなる。

画像のピクセルデータはそのままに解像度の設定値のみを変更したい場合、Ubuntu環境であればImageMagickを用いるとよい。

インストール
$ sudo apt install imagemagick
解像度の確認
$ identify -verbose hoge.png | grep "Resolution\|Units"
  Resolution: 37.79x37.79
  Units: PixelsPerCentimeter

この場合、単位がDPI(ピクセル/インチ)ではなくピクセル/cmになっていることに注意。ピクセル/cmをDPIに換算するには2.54倍すればよい。つまり37.79ピクセル/cm = 95.99DPIである。

DPI値で表示するには

$ identify -units PixelsPerInch -verbose hoge.png | grep "Resolution\|Units"
  Resolution: 95.99x95.99
  Units: PixelsPerInch
解像度の変更

300DPIに変更するには

$ convert -density 300 -units PixelsPerInch hoge.png piyo.png
$ identify -units PixelsPerInch -verbose piyo.png | grep "Resolution\|Units"
  Resolution: 300x300
  Units: PixelsPerInch

下記の語順だと単位がDPIにならないので要注意。控えめに言ってバグだと思う。

$ convert hoge.png -density 300 -units PixelsPerInch piyo.png

Re:VIEWメモ

(1) 環境のインストール (on Ubuntu)

(1.1) Re:VIEWのインストール
sudo apt update
sudo apt install ruby
sudo apt install ruby-dev
ruby --version           ←バージョン確認
gem --version            ←バージョン確認
sudo gem install review
review version           ←バージョン確認
(1.2) TeX Liveのインストール
sudo apt install texlive-lang-japanese texlive-latex-extra texlive-latex-recommended
(1.3) md2reviewのインストール
sudo apt install build-essential    ←ビルドのために必要
sudo gem install md2review

(2) Re:VIEWのプロジェクト作成

プロジェクト名を指定するとその名前のフォルダにひな型ができる。

review-init プロジェクト名

(3) md2reviewの使い方

Markdownのファイル名とRe:VIEWのファイル名を指定して変換。

md2review 入力.md > 出力.re

画像に図表番号とキャプションをつけない場合

md2review  --render-disable-image-caption 入力.md > 出力.re

(4) PDFの生成

book.pdfを生成する。rakeはmakeみたいなやつ。

rake pdf

(5) RE:Viewソースの編集

改ページ

//embed[latex]{
\clearpage
//}

テーブルのはみだし
tsizeで列の幅を設定する。単位はmm。

//tsize[40,80]
//table[tbl2][]{

数式
MarkdownでHTMLを駆使してもmd2reviewは解釈してくれない。
Re:VIEWでは `@{}` という記法でTeXの数式がいちおう書ける。いっそ画像を貼ったほうが良いかも。

(6) RE:Viewの設定

(6.1) config.ymlの編集

書名と著者名
booktitleとautで指定。

イラストレーターや印刷所など

刊行日と発行履歴
dateとhistoryで指定。

本のサイズ
texdocumentclass で指定。

texdocumentclass: ["review-jsbook", "media=print,paper=b5"]

文字数、行数、フォントサイズ、余白など
texdocumentclass で指定。

  • 1行あたり文字数: line_length=40zw (40文字)
  • 1頁あたり行数: number_of_lines=30 (30行)
  • フォントサイズ: fontsize=10pt (10ポイント)
  • 行送り: baselineskip=16pt (16ポイント)
  • 天の余白: head_space=15mm
  • ノドの余白: gutter=27mm
texdocumentclass: ["review-jsbook", "media=print,paper=b5,gutter=27mm,line_length=40zw"]

章の偶数ページ開始を許容
デフォルトでは章は必ず奇数ページから開始する。そのためページ数合わせのために白紙ページが挿入されることがある。texdocumentclass で openany を指定すると偶数ページから章を開始することが許容され、白紙ページの挿入が行われなくなる。

texdocumentclass: ["review-jsbook", "media=print,paper=b5,openany"]

ヘッダ、フッタ、ページ番号など
texdocumentclass で指定。

  • フッタと版面の間隔: footskip=7mm
  • ヘッダと版面の間隔: headsep=5mm
  • ヘッダの高さ: headheight= 10mm
  • 大扉からの開始ページ番号: startpage=1
  • 大扉から通しのページ番号をアラビア数字で振る: serial_pagination=true
  • 隠しノンブルを振る: hiddenfolio
    • serial_pagination=trueと併用すること
    • hiddenfolio=default - ノド上部(トンボの外)にページ番号を小さく配置
    • hiddenfolio=nikko-pc - ノド中央にページ番号を小さく配置
    • hiddenfolio=marusho-ink - 塗り足し5mm、ほかは日光企画に同じ
    • hiddenfolio=shippo - ねこのしっぽ社用。設定は日光企画に同じ

目次として抽出する見出しレベル
toclevelで指定。2なら章と節まで。

toclevel: 2
(6.2) catalog.ymlの編集

RE:Viewソースファイルの追加

PREDEF: ←前付 (まえがきなど)

CHAPS: ←章
  - chap1.re
  - chap2.re

APPENDIX: ←付録

POSTDEF: ←後付 (あとがきや著者紹介など)
  - postdef.re
(6.3) sty/review-custom.styの編集

ページヘッダ
左ページに章タイトルのみ、右ページに節タイトルのみ表示する設定は以下のように書く。

\lhead[\gtfamily\sffamily\bfseries\upshape \leftmark]{}
\rhead[]{\gtfamily\sffamily\bfseries\upshape \rightmark}

フォント
IPAexフォントのフォントマップを用いる場合は以下のように書く。

\usepackage[ipaex]{pxchfon}

IPAフォント、IPAexフォント以外を指定したい場合は下記を参照。

オブジェクト初期化子

C#って、

    class Hoge
    {
        public int a;
        public int b;
        public int c;
    }

というクラスに対して

    var hoge = new Hoge() { a = 1, b = 2, c = 3 };

という初期化の書き方ができるらしい。いま知った。
オブジェクト初期化子というらしい。

    var hoge = new Hoge { a = 1, b = 2, c = 3 };

とも書けるようだ。
いや、意味はパッと見で分かるんだけど、文法知らないといささか面食らう。

EtherCATネタまとめ

個人的に今年取り組んだ最大のネタはEtherCATでした。今年の成果をまとめます。

f:id:licheng:20191228114758j:plain

ブログ記事 (はてなブログ)











ソース(GitHub)




動画 (YouTube)








スライド資料 (SlideShare)

EtherCATのSDOとかPDOとか

EtherCATに関する文章を読んでいて、何のことわりもなく唐突にSDOとかPDOとかいう異質な言葉が出て来て面食らったことはないですか?

SDOとかPDOとかってCANの用語じゃないの?

SDOとかPDOとかいうのは、EtherCAT用語というよりは本来はCANopen用語のはず。EtherCATの上位層プロトコルのひとつであるCoE(CANopen over EtherCAT)において用いられる概念です。…いや、たぶんそのはずなのですが、EtherCATの話の中で特にCoEとことわることなくSDOとかPDOとかいう言葉が使われることが多い感じがします。

そもそもCANopenというのは、その名から分かるようにCANの上位層プロトコルとして作られたものですが、EtherCATの上にも載ります。それがCoE(CANopen over EtherCAT)です。さらにCANopenの上位層として、例えばモータ等を制御するCiA402ドライブプロファイルなどがあります。

イメージとしては下図のような感じです。(わりとテキトーに描いた図なのでレイヤーの高さはいいかげんかも…)

f:id:licheng:20191225123152p:plain

SDOとPDOのちがい

まあ、SDOとPDOについての説明はさんざん既出だと思うので、こんな辺鄙なブログでわざわざ語るようなことでもありませんが、いちおう簡単に触れておきます。SDOはマスターが通信したいときに通信したいスレーブに要求を投げてデータを書いたり読んだりする通信です。それに対してPDOはマスターと全スレーブが予め示し合わせて周期的に一斉に読み書きをする通信です。言うまでもなく、PDOを使ってこそEtherCATの本領である低レイテンシ・低ジッタを発揮できます。

CiA402

モータ等を制御するCiA402ドライブプロファイルを例にとってみましょう。CiA402には以下のようなOD(オブジェクトディクショナリー)が定義されています。まあ、BLEでいうキャラクタリスティックの定義のようなものです。IndexというのはBLEでいうUUIDのようなものです。そして各ODにはデータ型(整数型や浮動小数点型など)、アクセス(Read&WriteかRead Onlyか)、名前(それが何のデータか)が定義されています。
そして、SDOでは全てのODのデータにアクセスできますが、PDOではすべてのODのデータを周期通信データにマッピングできるわけではありません。 CiA402のODから一部抜粋して下表に示します。

Index(Hex) Name Type Access PDO Mappable?
6040/0 コントロールワード UINT16 RW Yes
6041/0 ステータスワード UINT16 RO Yes
6060/0 動作モード INT8 RW Yes
6071/0 目標トルク INT16 RW Yes
6072/0 最大トルク UINT16 RW No
6077/0 現在トルク INT16 RO Yes

お分かりでしょうか? この中では最大トルクがPDOにマッピングできませんね。目標トルクや現在トルクは常々設定・取得し続けなければならないものでPDOで通信すべきです。それに対して、最大トルクというのはふつうは一度設定すればそうそう変更するものではありません。だから最大トルクはSDOでのみアクセスとなっているのでしょう。まあ、それ言うと動作モード(トルク制御、速度制御、位置制御などのモード)だってそうそう変更するものではないと思いますが…?

今日の駄文はここまでです。わりとテキトーなことを書いたので詳しくはちゃんとした仕様書を読んでください。

ExcelマクロでEtherCATの制御

EtherCATについて語る Advent Calendar 2019 はもう埋まりそうなので、こちらはニッチすぎる番外編記事です。

やりたいこと

オープンソースのEtherCATマスターであるSOEMをDLL化し、ExcelVBAマクロから呼び出せるようにする。これによって、ExcelからEtherCATスレーブの制御が可能となり、Excel上で自動計測とデータ処理ができるようになる。

SOEMのDLL化

SOEMのDLL化については、以前の記事「EtherCATマスターSOEMを.NETで使う」に書いた。ここで作成したsoemlib.dllを使用する。(ただし、今回DLLをExcelから呼び出すにあたって不都合があったので少し加筆修正した。)
lipoyang.hatenablog.com

Excelマクロからの呼び出し

VBAで下記のようにDLL(soemlib.dll)のAPIを宣言する。

' soemlib.dll のAPI
Declare Function soem_open Lib "soemlib.dll" (ByVal nic As String) As Integer
Declare Sub soem_close Lib "soemlib.dll" ()
Declare Function soem_config Lib "soemlib.dll" () As Integer
Declare Function soem_transferPDO Lib "soemlib.dll" () As Integer

ただし、DLLはパスの通っているディレクトリにないと読み込めない。パスの通ってないディレクトリにDLLを置く場合には下記のようにしてDLLを読み込む必要がある。

Private Declare PtrSafe Function LoadLibrary Lib "kernel32.dll" _
            Alias "LoadLibraryA" (ByVal fileName As String) As Long

' 初期化
Sub ethercat_init()
    ' soemlib.dll の読み込み
    LoadLibrary ("C:\tool\SOEM\build\test\linux\soemlib\soemlib.dll")
    
End Sub

あとは、ふつうにVBAからDLLのAPIを呼び出すことができる。

    ret = soem_findAdapters()
    If ret = 0 Then
        MsgBox ("ネットワークアダプタが見つかりません")
        Exit Sub
    End If

自動計測の実験

ためしに簡単な自動計測装置を作ってみた。Arduinoで作ったEtherCATスレーブ(上記記事を参照)にマイクロサーボと可変抵抗を接続し、マイクロサーボと可変抵抗の回転軸を連結した。

f:id:licheng:20191220221316p:plain

f:id:licheng:20191220232828j:plain:w480

サーボの角度を0°から180°まで10°ずつ変化させ、そのときの可変抵抗のADC値を測定するマクロを以下に示す。

' 計測
Sub ethecat_measure()
    
    ' サーボの角度 0°から180°まで10°ずつ
    For i = 0 To 18
        ' 10msec周期で100回繰り返す
        For j = 0 To 100
            ' サーボの角度
            deg = i * 10
            soem_setOutputPDO 1, 0, deg
            ' 転送
            ret = soem_transferPDO()
            ' 可変抵抗のADC値
            h = soem_getInputPDO(1, 0)
            l = soem_getInputPDO(1, 1)
            volume = (h * (2 ^ 8)) + l
            ' 10msec待つ
            Sleep (10)
            ' これを入れないと画面が固まる
            DoEvents
        Next
        ' 表へ記入
        Cells(2 + i, 1).value = deg
        Cells(2 + i, 2).value = volume
    Next
End Sub


これを実行すると自動計測がおこなわれ、下図のようなグラフが得られた。

f:id:licheng:20191220223705p:plain

注意事項

  • VBAは昔のVB6と同じでスレッドは使えない。なので時間のかかるループ処理ではDoEventsを入れないと画面が固まる。
  • 上の例では10msecごとに通信をおこないつつ、サーボの(メカ的な)応答待ちのために1秒待っている。通信せずに1秒待つとなぜかOutputPDOの値がリセットされてしまう。マスター側でリセットされるのかスレーブ側でリセットされるのか、不具合なのか仕様なのか、要調査。

動画


ソース

今回作成したマクロ入りExcelシートを参考までに置いておく。

また、EtherCATスレーブのソースは下記のものを使用した。
github.com

ロボットの体内ネットワークとしてのEtherCAT

この記事は EtherCATについて語る Advent Calendar 2019 の19日目です。

昨日は@nonNoiseさんの EtherCAT 動画集(デモストレーション編) でした。


【注意!】 今日の記事はかなり偏った見解を含んでいる可能性があります。

今日はロボットに使われる通信方式のお話です。多足歩行ロボットや車輪走行ロボットのようなロボットの体内にはさまざまなセンサ、モータ、マイコン、無線モジュールなどが搭載され、それらは互いに有線で接続され通信しあって動作します。そのような通信網をここではロボットの体内ネットワークと呼ぶことにします。ロボットの体内ネットワークには種々の通信方式が用いられます。

f:id:licheng:20191217202917p:plain

ロボットの体内ネットワークによく用いられる通信方式を以下に示します。

通信方式 通信速度 リアルタイム性 通信距離 コスト・
難易度
上位層
プロトコル
UART ~数Mbps程度 ~1m程度
I2C ~400kbps ~1m程度
SPI ~20Mpbs程度 ~1m程度
RS-485 ~10Mpbs程度 ~1200m
(@100kbps)
CAN ~1Mbps ~40m
(@1Mbps)
Ethernet
(TCP/IP)
~1000Mbps × ~100m
EtherCAT ~100Mbps ~100m

UART

f:id:licheng:20191216203031p:plain

いわゆる「シリアル通信」です。昔からある通信方式で、今でもほとんどのマイコンに搭載されています。

基本的には一対一の双方向通信となります。マイコンうしの通信や、無線モジュールとの通信に用いられることが多いです。デバッグや実験のためのログ出力に用いることも多いでしょう。

USBシリアル変換ICをUSBハブに挿しまくって仮想シリアルポートだらけの一対多通信(?)をするという荒業もあります(笑)

I2C / SPI

f:id:licheng:20191216203051p:plain

I2CとSPIはUARTとならんでよく使う通信方式です。これらもほとんどのマイコンに搭載されています。

近ごろではI2CやSPIのインターフェースを持ったセンサICやモータドライバICなどが安く手に入るようになり、Arduinoとかでライブラリが用意されることが多いので趣味のロボット開発でもよく使われています。

しかし、I2Cはオープンドレインという電気的な仕様上、高速通信には向きませんしノイズにも弱いです。また、I2Cは1個のスレーブがトチ狂ってバスを握ってしまうとネットワーク全体が死ぬという弱点があります。I2Cは基板上の通信かごく短い基板間の通信に使うものです。長く伸ばしたI2Cケーブルをモータの電源ケーブルと束ねたりするのはホントにやめてください!

SPIはI2Cに比べれば高速で、ノイズ耐性もマシとはいえ決して強いとはいえません。

RS-485

f:id:licheng:20191216203108p:plain

UARTにRS-485トランシーバを接続することで実現できます。半二重通信になるので通常のUARTよりやや処理が面倒になりますが、さほどの難易度ではありません。

差動信号を用いるので通常のUARTやI2C、SPIに比べてノイズ耐性は高く、通信距離を長く伸ばせます。ホビーロボットではあまり用いることがありませんが、ある程度本格的なロボットではよく用いられます。一部のシリアルサーボにはRS-485を用いるものがあります。

上位層のプロトコルは特に標準化されてないので、オレオレプロトコルになりがちです。オレオレプロトコルの問題点は同一バス上で共存できないことです。いちおうModbusというFA業界でデファクトスタンダートなプロトコルもあるにはあります。

CAN

f:id:licheng:20191216203126p:plain

車載で用いられる通信方式です。最近ではマイコンに搭載されることも多くなりましたが、やはりCANを搭載するのはミドルレンジ以上のマイコンが多いように思います。

今まで挙げた通信方式と比べてソフトウェアの処理がすこし複雑です。上位層のプロトコルとしてCANopenが存在します。ハイエンドなモータドライバにはCAN/CANopenに対応したものがあります。RS-485と同様に、ホビーロボットではあまり用いることがありませんが、ある程度本格的なロボットではよく用いられます。

ただし、最大1Mbpsと通信速度が低いのが難点です。

Ethernet (TCP/IP)

f:id:licheng:20191216203140p:plain

Ethernetについては説明不要でしょう。やはりEthernetを搭載するマイコンというとハイエンド寄りのものに限られます。ソフトウェア処理の負荷も、今まで挙げた通信方式と比べて各段に高いです。加えてリアルタイム性が無いというのがロボットの制御にとっては致命的な欠点です。スループットこそ抜群に高いEthernetですが、ロボットの制御においてはスループットの高さよりもリアルタイム性 (レイテンシの低さ)こそが命です。

EtherCAT

f:id:licheng:20191216203158p:plain

そこで我らがEtherCATです。

Ethernet(100BASE-T)に基づく100Mbpsの通信容量を無駄なく使う高いスループットと低いレイテンシを両立し、通信距離もノード間最大100mとロボットの体内ネットワークとしては十分すぎる長さであり、差動信号のためノイズ耐性もあり、そしてCoE(CANopen over EtherCAT)という標準化された上位層プロトコルが用意されています。ハイエンドなモータドライバにはEtherCATに対応したものがあります。

欠点は、部品コストが高いこと、ソフトウェア負荷が高いこと、技術的な難易度が高いこと、そして、やってる人が少ない (=情報が少ない) ことです(涙)。

なぜそんなにEtherCATを使いたいか?

ロボットの制御とは物理の計算であり、物理の計算とは微積分です。時間微分・時間積分を正確に計算するためには⊿Tが可能な限り正確で可能な限り小さいことが求められるのです。⊿Tとはすなわちデータ更新周期であり、すなわちそれは通信周期です。

f:id:licheng:20191217231425p:plain

そしてロボットは多数のセンサとモータのかたまりです。そのため通信量も大きくなります。かりに10個のモータ、10個のセンサと各々16バイトずつの通信をしたいとします。通信周期を1msec(1000Hz)にするには、(10+10)×16×8×1000 bps = 10.24Mbps以上の速度が最低限必要になります。

こうした理由で、低レイテンシ・低ジッタと高スループットを両立できるEtherCATが望まれるのです。

EtherCATが使われるケース

現状、EtherCATが用いられるロボットというのは、大きさでいえば大型犬サイズ以上、ご予算でいえば少なくとも100万円超えの規模になると思います。例えば3万円のモータと3万円のモータドライバを16軸使ったロボットを作ろうとするとそれだけで(3+3)万円×16軸=96万円になりますよね。

投げ売り2万円のプリメイドAIに群がってサーボモータをむしり取るような私たちとは次元の違う世界です。産業用ロボットを除けば、ほぼ研究開発用ロボットにかぎられるんじゃないかと思います。数万円~数十万円台のホビーロボットにEtherCATを用いることは今のところ非現実的です。

…が、10年後においては定かではない。(願望)

参考文献

  • 神永拓, 中村仁彦「油圧駆動ヒューマノイドロボットHydraのエレクトロニクス」
    第33回日本ロボット学会学術講演会, 2015