USB-CANでmibotのソフトウェアテストを自作してみた
はじめに
こんにちは!KGモーターズ株式会社でエンジニアをしている中村です。
KGモーターズは、今日より明日が良くなる未来を創る をビジョンに掲げ、広島を拠点に1人乗り小型 EV mibot を開発しているスタートアップです。
車のソフトウェア開発のテストについて、最終的には車に載せて確認することは必須ですが、開発段階で毎回車に載せて...テスト...を繰り返していたらあまりにも時間がかかってしまうため、少しずつスケールしたテストをしていくことが一般的です。
その中でも今回は車載コンピューター/アプリケーション周りのテストについてです。
開発途中のmibotのディスプレイアプリを少し公開します!
またCAN通信とはなんぞや?も解説しています!
全体像
mibotにはユーザーが車の状態を確認できるディスプレイアプリを搭載しています。
見づらいですがYoutubeのショート動画にあるこれ👇です。

このディスプレイアプリですが、通常のアプリと異なる点の1つとして
「車両状態をユーザーにわかりやすく、リアルタイムで表示する」
という点が挙げられます。
イメージとしては
- 今何キロで走っているのか?
- ドアはロックされているのか?
- エアコンやシートヒーターはついているのか?
などなどです。このテストは普通に考えると車にアプリを搭載して確かめる、となるのですが、開発効率が大きく下がってしまうので、
「車両が出すべき信号を模擬的に出して、アプリ上に表示する」
というテストが必要になります。
上記のテストをするために、mibot電装チームに教えてもらい、以下のUSB-CANを購入して検証環境を作りました。
簡単なアーキテクチャ
テストの構成図が以下です。
左は、実際にmibotにディスプレイアプリを載せる車載コンピューターを繋げてテストする場合で、右が今回の場合です。
車がPCにすり替わってスッキリしています。
(実際には車載コンピューターはmibotの車体の中にいますが、今はわかりやすく外に書いています)

図の中で、車載コンピューターにCANという線が繋がっていますが、CANは車業界では一般的な通信方式で、mibotでも使用しています。
CANの解説は後段で行います。
試験の模様

とんでもなく地味でわかりづらいのですが、
- UIでシートヒーターボタンを押すと、それに対応したCAN信号が受信でき、データが反映される
- シートヒーターレベルをCAN信号して送ると、UIが連動して変わる
という感じです。

用いた開発環境
Macでできないのが最高にきついのですが、ドライバーの関係もありWindowsで実行しています。
またPythonの以下のライブラリを使うことで簡単に実装できます。
- python-can
- CAN 通信(送受信)を Python から行うためのライブラリ
- USB 経由でハードとつなげることができる
- cantools
- DBCファイルを読み取ってデコード・エンコード処理をする
ちなみにDBCファイルとはCAN 信号仕様を定義したファイルのことです。ただの0/1信号を「これは何の信号で、意味は何か?」と解釈するのが DBCファイルの役割になります。
CANについて
CANとはController Area Networkの略で、自動車でよく用いられる通信規格です。
Bosch社によって1986年に仕様が公開され、1994年には国際規格(ISO 11898)となって、現在ではもっとも普及している車載ネットワークプロトコルです。
最近だとイーサネットに置き換わるという話もありますが[1][2]、現状はまだまだCANが使用されています。
「CAN通信 解説」などとGoogleで調べると色々記事が出てきますが、おそらくベクター社の以下のPDFをベースに書いてあるものがほとんどな気がしています。
CANのメリットと特徴
バス通信
車の中には多くのコンピューター(ここでは電子制御ユニット: ECU と呼びます)が搭載されていますが、制御の内容やセンサーが増えていくと入出力も増えていき、さらにECU間でデータを共有すると配線が増加していきます。
そこでCAN通信でワイヤーハーネスの代わりにバス通信を使うことで、配線コストの削減と自動車の軽量化が実現できたというわけです。
車の中には多くのコンピューター(電子制御ユニット:ECU)が搭載されています。
しかし制御の内容やセンサーが増えると、ECU間でやり取りするデータもどんどん増えていきます。
もしECU同士をすべて個別に配線すると、ECUの数に応じて配線は爆発的に増え、クルマの中が線だらけになってしまいます。
そこで登場したのが 「バス通信」 という仕組みです。
バス通信は、複数のECUが 1本の共通の通信線(バス)を共有して通信する方式 です。
この仕組みにより、ECU間の配線が劇的に減り、配線コストの削減や自動車の軽量化を実現できたというわけです。

計測器ラボ[3]より抜粋
ライン型構造
一般的にネットワークに接続される通信機器を「ノード」といいます。上の例だとECUがノードになるわけですが、複数のノードを接続してネットワークを実現する方式としては大きく以下があります。
- スター型
- 全ノードが1箇所(ハブ)に接続する方式
- 接続経路がシンプルで高速
- しかしハブが故障すると通信全体が止まるデメリットがある
- リング型
- ノード同士が環状につながる方式
- 迂回経路が作れるため信頼性は高い
- 一方で構成がやや複雑になる
- ライン型(バス型)
- 1本の通信線(バス)を全ノードが共有する方式
- 全ノードが1本の線にぶら下がるイメージ

はじめてのCAN/CAN FD[4]より抜粋
CANで用いられているのはライン型と呼ばれる方式です。
このライン型は、バスにノードを接続していくことでネットワークを構成できるので設計がシンプルになるというメリットがあります。
もともと車にCANが採用されたのは上でも書いたように複雑な配線をシンプルにするためという目的があるので、ライン型は目的にもあったネットワーク構造だと言えます。
ライン型にすると、後からノードを追加しやすかったりと開発面でもメリットがあります。
マルチマスター方式
CANは、ライン型で接続されている各ノードに対して平等に情報を送信が可能なマルチマスター方式を使っています。
マルチマスター方式を使うことで、開発時に各ノードを同一仕様で設計することができ、データを必要な場合に送信することが可能になります。
さらに各ノードに優劣が無いため自由度の高いネットワークを構成することができます。
要はクラスのみんながベラベラ発言することができるみたいなイメージですね!(雑)

はじめてのCAN/CAN FD[4:1]より抜粋
優先度制御
CANはマルチ・マスター方式を採用しているので、どのノードも自由にデータを送信できます。
しかし、
- あるノードが送信している時に、別のノードが送りたい場合どうなるのか
- もし複数のノードが同時に送信しようとした場合どうなるのか
などの懸念が出てきます。
先ほどのクラスの例でいうと、
- A君がめっちゃ喋ってる時にBさんが喋りたい場合
- A君とBさんが同時に喋りたい場合
このような感じで、要は衝突した時どうするか?という話です。
これを解決するために、CANではドミナントとレセシブという仕組みを使って
- CSMA/CA
- Arbitration
により衝突回避を実現しています。
ドミナントとレセシブ
CANのデータは0と1の2進数でバスに送信されます。CANではこの2進数を
-
0: ドミナント(優性) -
1: レセシブ(劣性)
で表現し、ドミナントが優先されるようにします。
と巷の記事でよく見にするこのドミナントとレセシブですが、最初見た時は「だからなんだよ?」でした。
これがどうやって衝突回避につながるのか、以下で具体的に見ていきます。
Arbitration
これは同時に送ってしまった時の話です。
CANではデータにIDを付与し、どのノードのデータかを識別しています。
このIDも二進数で表現されます。
簡単のため、ここではIDが4bitの場合を例にします。
(実際には11bitまたは29bitです。)
- IDが10進数で6
0110
- IDが10進数で9
1001
この2つのIDが同時に送信された場合、先頭からビットを比較します。
先に0を送った方(ドミナント)が勝つため、この場合はID=6のノードが優先されます。
つまり、ドミナントとレセシブを使うことで、IDが小さい方が優先される仕組みを作れるというわけです。
CSMA/CA
CANは、複数のノードが自由にデータを送信できるため、誰かが送信している最中に別のノードが割り込んで衝突するリスクがあります。
これを回避するために使われるのがCSMA/CAです。
CSMA/CAは以下のように機能します:
- バスがアイドル状態(
レセシブ = 1)のときは送信してOK - バスが
ドミナント(0)のときは「誰か通信中だ」と判断し、送信を控える
これにより、すでに誰かが通信中のところに別のノードが割り込むことを防いでいるのがCSMA/CAの役割です。
ただし、それでもバスが空いてると複数のノードが同時に判断して送信を始めてしまう場合がありますが、このときは Arbitration によって、優先度(IDの大小)で勝敗が決まる仕組みになっています。

はじめてのCAN/CAN FD[4:2]より抜粋
ノイズに強く高い信頼性
自動車での使用を前提に開発された通信プロトコルのため、ノイズに強いという特性があります。
2本の通信線に発生する電圧差の有無によってデータを送信する差動電圧方式を採用しているため、外部から加わるノイズは同一のため電圧差が起こらず、ノイズが発生しても影響を受けにくくなっています。

はじめてのCAN/CAN FD[4:3]より抜粋
CANのフレームタイプ
CAN通信では、バス上を流れる信号はすべて「フレーム」と呼ばれる単位で構成されています。
このフレームには以下の4つのタイプがあり、それぞれ異なる目的と構造を持っています。
- データフレーム:実際のデータを送る
- リモートフレーム:データを要求する(中身なし)
- オーバーロードフレーム:前回のフレームの処理がまだ完了していない時に、次のフレームの開始を遅延させる
- エラーフレーム:通信に問題があったことを全ノードに通知する
ここでは、最も基本かつ重要な「データフレーム」について詳しく解説します。
データフレーム: 標準フォーマット
データフレームは、実際のセンサ情報や制御パラメーター等のデータを送信する役割を果たします。
このデータフレームには標準フォーマットと拡張フォーマットの2種類あります。まずは標準フォーマットから見ていきます。
標準フォーマットの構造は以下のようになっています。

計測器ラボ[3:1]より抜粋
色々書いてありますが、全てただの0/1信号です。これをどのように解釈するかのルールを定義してることを頭の片隅に置いておいてください。
上図で、上の線はレセシブ、下の線はドミナントを表しており、ドミナント(もしくはレセシブ)にしか線がない場合はドミナント(もしくはレセシブ)固定、両側に線がある場合は送信データによって変化します。
例えばSOFには下にしか線がないので必ずドミナント(=0)が送られる、コントロールフィールドには上の線と下の線のどちらもついているため、データによって0 or 1どちらの可能性もあり得る、というわけです。
またバスアイドルというのは、CAN通信が行われていない状態のことを指します。この時、レセシブ(1)となっています。
そしてそれぞれの名前の下に数字が振ってあると思いますが、これはビット長で、何Bit使用するかを表しています。
ではそれぞれの説明です。
SOF
Start of Frameのことです。
フレームの開始を意味し、、バスアイドルのレセシブからドミナントへ変化することで、受信側ノードは同期を開始できるようになります。
ID
メッセージ識別子で、上で説明した優先度の順位判定にも使用されます。
標準フォーマットのため11bitで、つまり2048通りの識別が可能となっています。
RTR
Remote Transmission Requestのことで、データフレーム or リモートリクエスト を判別するために用いられます、データフレームの場合はドミナント(0)になります。
コントロールフィールド
分解すると以下のようになります。
- IDE(Identifier Extension)
- 1ビット長
- 標準 or 拡張フォーマットの判別用
- 標準フォーマットの場合はドミナント
- r0
- 1ビット長
- 予約ビット
- データレングスコード(DLC:Data Length Code)
- 4ビット長
- この後に続くデータ部の長さ(0〜8Byte)を4Bit(0 ~ 15までの数字)で表す
データ
実際に運ばれるデータで、最大8Byte(=64Bit)の長さを持っています。
この中でどのようなデータを送るかは開発者が自由に決めることができます。そのため1Byteずつセンサーデータを割り振ることもあれば、もっと細かく1Bitずつデータを割り振ることもできます。
CRC
データが壊れていないかをチェックするための検査ビットの役割です。
ACK
Acknowledgementのことで、受信側が正常に受け取ったことを示す確認応答のことです。
送信側はここでレセシブ(1)を送信します。
受信側はCRCまで正常に受信できたらドミナント(0)を送ります。
何をしているかというと、
- 送信側はレセシブにしてバスをあけておく
- 受信側はドミナントを返す
これによりレセシブよりドミナントが優先されるルールから、送信側は「誰かが正常に受け取った」と判断できるようになります。
(ただ1ビット長なので、「誰が」正常に受け取ったかまでは判断できないことに注意です)
EOF
End of Frameのことで、データフレームの終わりを表します。
データフレーム: 拡張フォーマット
標準フォーマットの次は拡張フォーマットです。
標準フォーマットのとの違いは、11BitだったIDが29Bitになっており、その内訳は以下のようになっています。
- ID: 11Bit
- 標準フォーマットのIDと同一
- SRR: 1Bit
- Substitute Remote Requestで、1Bitのレセシブ
- IDE: 1Bit
- 標準フォーマットで説明済みの標準 or 拡張の判断用
- 拡張ID: 18Bit
- ここが追加ID部分で増えた分だけ多くのIDを識別することができる

計測器ラボ[3:2]より抜粋
最後に
現在このような環境を社内で作りながら効率的なテスト環境を作っている最中です!
またこのような開発を一緒に作る仲間を募集していますので、
mibotのソフトウェア開発に興味のある方はぜひお気軽にご連絡ください!
Discussion