マイコン&距離センサでプラレールの自動ブレーキ制御

2歳の子どもが遊ぶプラレールが、しばしば前方車両や壁に衝突するのをみて(そもそも対象年齢じゃないのですが^^;)、自動で減速するように改造できないかと思っていたところにこちらを発見。電子工作は経験が浅いのですが、自分でもトライしてみました。

 

製作物紹介(使用部品、回路図)

上記リンクの例に倣って先頭車両の単2電池の代わりに単4電池2本と電子回路を収めました。マイコンは手元にあったATtiny2313、赤外線距離センサは最大10cmまで計測可能なVL6180Xを搭載したセンサモジュールを選びました。マイコンと距離センサはI2Cで接続します。電源はDC-DCコンバータで電池の3V(充電式なら2.4V)を3.3Vに昇圧してマイコンとセンサに供給し、モータードライバICには電池から直接供給します。

 

f:id:mzmlab:20210718084102j:plain

少しわかりづらいですが、フロントのセンサ部分をくり抜いています。

 

f:id:mzmlab:20210718084156j:plain

基板1枚には収まらなかったため、マイコンの上側にモータドライバICをスタックしています。

ATtiny2313はプログラム書き込み用のポートとI2Cポートが共用になっているため、距離センサはコネクタで取り外しできるようにしました。

 

回路図です。 

f:id:mzmlab:20210718084308p:plain

動画

youtu.be

動いている様子です。前方車両との接近を検出して減速しています(動画の後半は制御が間に合わず衝突してしまっているので、ソフトは改善の余地がありそうです)。なお、PWM制御により、モータにかかる電圧は1.5Vに制限しています。制限を外せばMAX 3Vまで上げられるので、カーブで脱線するくらい高速で走ることもできます(※モータの定格を超えるので壊れるかもしれません)。

 

以下のような応用も楽しそうですね。

・せっかくHブリッジなので、壁を検出するなどしたら後進させてみる。

・距離センサVL6180Xは環境光も測れるので、部屋が暗くなったらLEDを点灯する。

Bluetoothモジュールを搭載してスマホアプリから遠隔操縦できるようにする。

 

苦労したこと

・単2電池のスペースはそこそこ広く見えますが、実際やってみると電池ボックスと回路を収めるのにとても苦労しました。高さがぎりぎりで、部品を出来る限り低く付けたり電池ボックスの底を削ったりしてなんとか収めました。

 

・単4電池2本(3V)ですべての電源を賄うのも、初めてだったので少し迷走しました。最初に選定したモータドライバICTB67H450FNG)が4.5V以上でないと動作しないことに後で気付き、低電圧動作するNJU7386を選びなおしました。

 

・ATtiny2313フラッシュメモリ2Kバイト、RAM 128バイトという制限が、これまた思いの外厳しいです。デバッグ用のコードをいろいろ入れたせいでもありますが、すでにフラッシュメモリのプログラム領域は97%を使用しています。RAMも小さいため、スタックのサイズに気を付けないと、関数を入れ子にしたり割り込みをかけたりしたときにプログラムが暴走しました。もし同じことをしてみたいという方がいらっしゃれば、もう少し高スペックなマイコンを選んでもよいかもしれません。

 

このように思いの外苦労したので、2歳の怪獣に壊されるのが惜しくなってきました()。見せてあげるのはもう少し対象年齢に近づいてからにしたいと思います…

 

参考情報

ATtiny2313の開発環境構築

こちらで一通り解説してくださっており、迷わずプログラム書き込みまでできました。

なお、開発環境Microchip Studioは新しくなっているのでこちらを参考にダウンロード、インストールしました。

なお、今回はFuseは出荷時のままで問題ありません。間違って書き換えると動作しなくなったりするのでご注意ください。

 

ATtiny2313I2C Masterの実装

ATtiny2313SCLSDAのポートがありますが、ハードウェアは最低限の機能しかなく、ソフトウェアでI2Cを実装する必要があります(自分も作りながら気付きました...)。ネットでいろいろ調べてみると、本家のサンプルコードがあったのでこちらを利用しました。

アプリケーションノート

サンプルコード

サンプルコードはサイト内で”I2C”で検索して出てくる”AVR310 USI as I2C master”をダウンロードし、その中のUSI_TWI_Master.cUSI_TWI_Master.hを自分のプロジェクトに追加すると使えました。

Unityで太陽系の簡易シミュレーション

過去2回に渡ってUnity万有引力による惑星の運動を実装してきましたが、それらを応用して太陽系を製作しました。UnityプロジェクトをGithubで公開します。シミュレーションというには簡易過ぎると思いますが、ご自由に活用ください。時間が取れれば製作過程についても少しずつ書いていきたいと思います。

Githubリポジトリ
ronron-gh / unity-space

■過去記事
Unityで万有引力による運動の確認 - mizu-mi blog
Unityで万有引力による運動の確認(2) ~月も回してみた - mizu-mi blog

f:id:mzmlab:20200204235554p:plain

 

 

概要

各惑星の自転と公転をUnityの物理エンジンでシミュレーションしています。シンプルに作りたかったため、惑星の大きさや軌道は現実に忠実ではありません。

◎こだわった点
・各惑星の運動のスクリプトは共通化した。
・月も実装した(衛星用のスクリプトを別途作成)。
・各惑星にはテクスチャを貼り付けてリアルな見た目にした(NASAが公開するフラット画像を使用)。また、土星の輪はBlenderで作成した。
・光は太陽の位置から全方位に照射。
スマホのジャイロでカメラを動かせる(カメラ用スクリプト内の#ENABLE_GYROを有効化してスマホ向けにビルドすることで使用可能。※Androidのみで確認)。

 

開発に用いたUnityバージョン

2018.2.15f1

 

使い方

GithubからcloneしたフォルダをUnityで開いてください。Assets->Scenes->SampleScene Hierarchyにドラッグ&ドロップすると、デバッグ実行できる状態になります。

また、demoフォルダに、Windows向けにビルドしたexeファイル(Windows10で動作確認済み)と、android用のインストーラ(.apk)が入っています。

 

テクスチャ画像の配布元

以下のサイトから集めましたが、配布元はNASAのようです。NASAの画像は通常著作権フリーのようですが、商用利用は自己責任でお願いします。

・地球
 Blue Marble Next Generation

・水星、金星、火星、木星天王星海王星
 CG講座POV-Ray地球

土星土星の輪
 JHT's Planetary Pixel Emporium

・月
 CGで月を作る | CGBeginner

 

ライセンスについて

著作権を主張するほどのものではありませんが、とりあえずMITライセンスとしています。ご自由にご利用ください。

 

ニューラルネットワークの表現力を確かめる

こちらで、「ニューラルネットワークは普遍性定理により任意の関数を表現できる」ということに触れました。今回は、実際に複雑な非線形の関数をニューラルネットワークに学習させ、表現力を確認したいと思います。

確認のために作成したプログラムはGithubに置きます。フレームワークchainerを利用しています(chainerはメジャーアップデートが終了してしまいましたね。勉強目的で使っているので気にしないことにします^^;)。
ronron-gh / neural-regress-sinc

■普遍性定理について
以下のリンクで直感的にイメージできる形で説明されています(「ニューラルネットワークと深層学習」というオンライン書籍の翻訳のようです)。
ニューラルネットワークが任意の関数を表現できることの視覚的証明

普遍性定理により、中間層が1層あればあらゆる関数を近似できるとされています。層がディープだから何でも近似できるのだと思っていたので、ちょっと衝撃です(^^;

ニューラルネットワークに学習させる関数は次のグラフに示すsinc関数とします。

f:id:mzmlab:20200126235813p:plain

中間層1層の場合

まずは、普遍性定理を確認するために、中間層を1層としました。中間層のニューロン数は500としました。ソースコードリポジトリnn_regress_sinc.pyを参照ください。

f:id:mzmlab:20200126235910p:plain

学習結果は次のグラフの通りです。やや歪ですが、sinc関数に近い結果が得られました(ニューロン数やエポック数などのハイパーパラメータを調整すれば、もっと良い結果が得られるかもしれませんが、ここで力尽きました^^;)。

f:id:mzmlab:20200126235953p:plain

中間層2層の場合

次に、中間層を2層に増やして学習させてみました。ニューロン数は1層目2層目共に100としました。ソースコードリポジトリnn_regress_sinc_mid2.pyを参照ください。

f:id:mzmlab:20200127000034p:plain

学習結果は次のグラフの通りです。中間層1層のときよりもニューロン数が少ないにもかかわらず、sinc関数をより忠実に表現できています。

f:id:mzmlab:20200127000106p:plain

まとめ

中間層が1層しかないニューラルネットワークでも、普遍性定理によりsinc関数のような複雑な関数を表現できることを、実際にプログラムを組んで確認しました。また、中間層を2層に増やすと、1層のときよりも少ないニューロン数でより良い学習結果が得られることを確認しました。

ニューラルネットワークと最小二乗法の違いについて考えてみた

ニューラルネットワークと最小二乗法は、”損失関数(二乗和誤差)が最小となる重みを探す”という点で似ているように感じ、違いをはっきりと理解したいと思ったため、両者の違いについて自分なりに考察してみました(機械学習初心者なので間違いがあるかもしれません)。

考察のために作成したプログラムはGithubに置きます。フレームワークはchainerを利用しています(chainerはメジャーアップデートが終了してしまいましたね。勉強目的で使っているので気にしないことにします^^;)。
ronron-gh / neural-regress

 

ニューラルネットワークで回帰問題を解く場合

次のグラフに示すサンプルデータの近似関数を導出する回帰問題を考えます。

f:id:mzmlab:20200102000603p:plain

まずは、最小二乗法は使わずに、ニューラルネットワークで解いてみます。非線形の問題なので次の図のように中間層を1層設けました。

余談ですが、中間層が1層だけでも、ニューラルネットワークはあらゆる非線形関数を表現できます。これを普遍性定理というようです。以下のWebページで直感的にイメージできる形で説明されています(「ニューラルネットワークと深層学習」というオンライン書籍の翻訳のようです)。
ニューラルネットワークと深層学習
それでも、層を深くしたディープラーニングが注目されているのにはいろいろと訳があるようです(自分も勉強中のため、これ以上はもっと詳しくなってから書きます^^;

f:id:mzmlab:20200102001452p:plain

結果は次のグラフのようになりました。中間層のニューロン数が増えると表現力が増していきます。

f:id:mzmlab:20200102001550p:plain

中間層のニューロン数:5

f:id:mzmlab:20200102001727p:plain

中間層のニューロン数:10

f:id:mzmlab:20200102001746p:plain

中間層のニューロン数:50

最小二乗法で回帰問題を解く場合

それでは、最小二乗法で解く場合を考えます。最小二乗法ではまずどんな関数で近似するかを決める必要があります。ここでは次のような三次関数で近似することにします。

 f(x) = a3x3 + a2x2 + a1x + a0

最小二乗法ではサンプルデータとf(x)の二乗和誤差が最小となる係数a0a3を求めることになります。表計算ソフトで最小二乗法を実行した結果を次のグラフに示します。

最小二乗法のアルゴリズムについては説明しませんが、自分が参考にしているWebページを紹介させていただきます。(Webで二次以上の関数の最小二乗法について説明しているのは、自分が知っている中ではここだけです)。
 ITエンジニアの数学 最小二乗法

 

f:id:mzmlab:20200102002057p:plain

このように、最小二乗法ではまず三次関数で近似できると仮定していますが、ニューラルネットワークではそのような仮定はしないため、両者は回帰問題へのアプローチの仕方が違います。

ニューラルネットワークで最小二乗法と同じアプローチを取ってみる

もう少し踏み込んで、今度はニューラルネットワークで最小二乗法とまったく同じアプローチを取るとどうなるかを考えてみました。それが次のニューラルネットワークです。

 

f:id:mzmlab:20200102002230p:plain

このように、最小二乗法は中間層なしで表現できました。結果は次のグラフの通り、先の表計算ソフトと同じような結果が得られました。このようにニューラルネットワークで表現すると、最小二乗法は実は(三次関数近似であっても)中間層なしで表現できるため、線形の問題を解いているに過ぎないということがわかります。

f:id:mzmlab:20200102002323p:plain

ニューラルネットワークで最小二乗法を解いた結果。重みは次の通り算出された(表計算ソフトの最小二乗法にほぼ一致)。 w3 = 0.298, w2 = 0.527, w1 = -4.010, b = 4.83

念のため補足しますが、ニューラルネットワークは複雑な問題に対応するために、損失関数(二乗和誤差)が最小となる重みの算出に勾配降下法を用いているため、計算時間は最小二乗法と比べ物にならないくらい大きくなります。一方、最小二乗法は解析的に二乗和誤差関数を偏微分して、勾配=0となる重みを求めるため高速です(最小二乗法の詳細は先に紹介したリンクを参照ください)。

まとめ

最小二乗法とニューラルネットワークの違いについて、自分なりに考察しました。最小二乗法は、ニューラルネットワークで解くことを考えると、線形の回帰問題を解くことに帰着します。一方ニューラルネットワークは普遍性定理により、どんなに複雑な非線形関数でも表現できるというポテンシャルを秘めています。
ゼロから独学で学び始めると、このような基本的な部分の理解にたどり着くまでに結構な時間がかかったため、記事にしてみようと思いました(どこか間違っていたらすみません)。

Unityで万有引力による運動の確認(2) ~月も回してみた

前回、地球が太陽から受ける万有引力によって楕円軌道で運動することをUnityで再現しましたが、今回は、さらに月を加えてみたいと思います。そんなの、月と地球の間に万有引力を発生させればいいじゃないかと思うかもしれませんが(自分も最初はそうしました)、実は太陽による万有引力も重要な役割を持っています。今回もパラメータやC#スクリプトを公開します。

 

月の公転の条件

冒頭で述べた通り、太陽の周りを公転している地球の周りを月が公転するためには、月に対して地球による万有引力と太陽による万有引力2つを加える必要があります(前回同様に単純化したいので、地球と太陽には万有引力は与えません)。

月も、地球と一緒に太陽の周りを公転していると考えると、太陽による万有引力を合成するのは自然な考えですね。

f:id:mzmlab:20191116000132p:plain

 

月の公転を実装

太陽、地球、月それぞれのパラメータと、C#スクリプトを示します。

パラメータ

万有引力定数
・太陽 Gs = 6.67E-06 Nm^2/kg^2  (実際の1E+05)
地球 Ge = 6.67 Nm^2/kg^2          (実際の1E+11)

 ※スケールがでたらめなので、万有引力定数は太陽に引かれる場合と
  地球に引かれる場合とで分ける。

■質量
・太陽 Ms = 2E+07 kg
地球 Me = 50 kg
月  Mm = 1 kg

■距離
・太陽と地球の距離 Rse = 20 m
地球と月の距離  Rem   = 5 m

■初速
・地球 Ve = sqrt(Gs×Ms / Rse) = 2.582634 m/s (太陽に対して垂直方向に与える)
月  Vm = sqrt(Ge×Me / Rem) = 8.167007 m/s (地球に対して垂直に与える)

 ※等速円運動となるvを求める。(式の導出は前回記事参照)

 

Unityコンポーネント設定

■太陽

コンポーネント 設定 備考
Transform Position X 0 m  
Y 0 m  
Z 0 m  
Rigidbody Mass 2E+07 kg  
Use Gravity チェックなし  

 

■地球

コンポーネント 設定 備考
Transform Position X 20 m  
Y 0 m  
Z 0 m  
Rigidbody Mass 50 kg  
Use Gravity チェックなし  
C# Script 初速(Z方向) 2.582634 m/s (=Ve) C#スクリプトのStart()で設定する。
太陽から受ける万有引力を計算して設定 C#スクリプトのFixed Update()で設定する。

 

■月

コンポーネント 設定 備考
Transform Position X 25 m  
Y 0 m  
Z 0 m  
Rigidbody Mass 1 kg  
Use Gravity チェックなし  
C# Script 初速(Z方向) 10.74964 m/s (=Ve+Vm) C#スクリプトのStart()で設定する。
地球から見た相対速度がVmとなるようにする。
太陽と地球それぞれから受ける万有引力を計算し、合成した結果を設定 C#スクリプトのFixed Update()で設定する。

 

C#スクリプト

月のオブジェクトにアタッチするC#スクリプトを示します。
地球については前回記事を参照ください。

public GameObject のsunとearthはUnity画面のInspectorビューに現れるので、太陽と地球のオブジェクトをドラッグ&ドロップしてください。万有引力や初速もpublicにするとInspectorビューから変更できるので便利かもしれません。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoonBehaviourScript : MonoBehaviour {
    //オブジェクト
    public GameObject sun;
    public GameObject earth;
    //パラメータ
    private float G_s = 6.67E-6f;    //万有引力定数(太陽)
    private float G_e = 6.67f;       //万有引力定数(地球)
    private float initVelocityZ = 2.582634f + 8.167007f;    //初速
    private float M_m;      //月の質量
    private float M_s;    //太陽の質量
    private float M_e;    //地球の質量
    private float r_s;    //太陽と月の距離
    private float r_e;    //地球と月の距離
    private float f_s;    //太陽から受ける万有引力
    private float f_e;    //地球から受ける万有引力

    void Start()
    {
        //質量の情報を取得
        M_m = GetComponent<Rigidbody>().mass;
        M_s = sun.GetComponent<Rigidbody>().mass;
        M_e = earth.GetComponent<Rigidbody>().mass;

        //初速を与える
        Vector3 initVelocity = new Vector3(0f, 0f, initVelocityZ); ;
        GetComponent<Rigidbody>().velocity = initVelocity;
    }

    void FixedUpdate()
    {
        //月から太陽に向かうベクトルを計算
        Vector3 direction_s = sun.transform.position - GetComponent<Transform>().position;
        //月から地球に向かうベクトルを計算
        Vector3 direction_e = earth.transform.position - GetComponent<Transform>().position;

        //距離rを計算
        r_s = direction_s.magnitude;
        r_e = direction_e.magnitude;

        //単位ベクトルに変換(後で万有引力の方向として使う)
        direction_s.Normalize();
        direction_e.Normalize();

        //万有引力を計算
        f_s = G_s * M_m * M_s / (r_s * r_s);
        f_e = G_e * M_m * M_e / (r_e * r_e);

        //加速度を与える
        GetComponent<Rigidbody>().AddForce((f_s * direction_s) + (f_e * direction_e), ForceMode.Force);
    }
}

実行結果

うまくいくとこのようになります。ちなみに地球の公転が速すぎると月がどこかへ飛んで行ってしまいました。いろいろパラメータをいじってみると面白そうですね。

f:id:mzmlab:20191116110746g:plain

 

実行結果~太陽による万有引力を省略した場合

太陽の必要性に気付くまで、しばらくこの状態でハマってた^^;

f:id:mzmlab:20191116111012g:plain

 

スマホから取り込んだ動画をFFmpegで圧縮するバッチファイルを作ってみた

最近家族からスマホAndroid)内の写真・動画をPCに取り込んで欲しいと頼まれたのですが、全部で40GB程あり、自分のPCHDD容量をかなり圧迫するサイズでした。この際RAID対応NASを導入しようかとも思いましたが、少々値が張るので、とりあえずサイズの大部分を占める動画(mp4)を圧縮してみることにしました。動画のサイズを見てみると、1分程度の動画で100MBを超えており、FFmpegで圧縮すると5分の1程度のサイズにすることができました。

バッチファイルの勉強を兼ねて、指定したフォルダ内のmp4ファイルをまとめて圧縮するバッチファイルを作成したので、せっかくなので公開したいと思います。

 

 

前置き① スマホからPCへの取り込みについて

詳細は割愛しますが、スマホUSBケーブルでPCに接続し、スマホ側でUSB転送モードをMTPに設定すると、PCエクスプローラからスマホ内(内臓ROM及びSDカード)のフォルダを参照できます。例ですが、SDカードのDCIM > 100ANDRO といった場所にスマホで撮影した写真・動画が格納されています。自分はフォルダごとPCHDDにコピーしました(デジカメの付属ソフトのように日付毎にフォルダ分けしてくれたりするツールは、探しても見つからないですね)。

 

前置き② FFmpegについて

FFmpegのダウンロードや使い方はインターネットで検索するとたくさん出てくるので割愛します。自分はこちらのサイトを参考にさせていただきました。

creatorsblog.nijibox.jp

 

前置き③ Google フォトの「バックアップと同期」について

Androidスマホを使っている方なら、Googleフォトの「バックアップと同期」を使えば、わざわざ自前でバックアップを取る必要がないことにお気付きかと思います。「バックアップと同期」は、容量無制限でオンラインストレージに写真・動画をバックアップでき、スマホ本体からはバックアップ済みの写真・動画を削除できます。また、バックアップした写真・動画は別のデバイスからも見ることができます。自分も「バックアップと同期」を使っていますが、念のために自分のPCや外付けHDDにバックアップを取っています。

 

作成したバッチファイル

引数で指定したフォルダパスにある動画(mp4)をすべて圧縮するバッチファイルを作成しました。対象フォルダをバッチファイル(.bat)にドラッグ&ドロップすることでも、フォルダパスを引数として渡せます。また、次回スマホから追加で取り込んだときに、同じファイルを圧縮しないようにするために、圧縮完了したmp4ファイルのファイル名をテキストファイル(compressed_file_list.txt)に保存し、次回はcompressed_file_list.txtにあるファイルは圧縮しない仕組みにしました。以下に、バッチファイル内容と解説を載せます。

※元ファイルは削除する(ゴミ箱にも残らない)ので、試す場合は念のためバックアップをお願いします。

 

バッチファイル内容

解説

@ECHO OFF
setlocal enabledelayedexpansion

IF "%1" == "" GOTO ERROR

ECHO
入力されたフォルダパス:%1
CD %1

SET is_comp=0

FOR %%a IN (*.mp4) DO (


  
FOR /F %%b IN (compressed_file_list.txt) DO (
    IF %%b==%%a (
      SET is_comp=1
    )
  )

  
IF !is_comp!==0 (
    ECHO %%a
を圧縮
     RENAME %%a %%a.tmp
    ffmpeg -i %%a.tmp -c:v libx264 -c:a aac %%a
    DEL %%a.tmp
    ECHO %%a >> compressed_file_list.txt
  ) ELSE (
    ECHO %%a
は圧縮済み
  )

  
SET is_comp=0
)

GOTO END

:ERROR
ECHO 入力エラー
SET /P inp = "何かキーを押して終了"

:END
SET /P inp = "何かキーを押して終了"


環境変数is_comp)の値を処理の途中で変更可能とするために遅延環境変数を使用する。


カレントディレクトリを入力されたフォルダパスに変更する。



フォルダ内のmp4ファイル名についてループ処理を行う。

compressed_file_list.txtにファイル名が存在する場合はフラグ(is_comp)を立てて圧縮処理をスキップする。



ファイル名を”ファイル名”.tmpにリネームし、ffmpegに入力する。ffmpegの出力は元ファイルの名前にすることで、圧縮前後でファイル名が変わらないようにする。
圧縮が終わったら、.tmpは削除し、元ファイルの名前をcompressed_file_list.txtに記録する。

 

 

 

 

 

補足

圧縮率が期待外れな場合の対処法

スマホの機種によっては、圧縮後のサイズがほとんど変わらない場合があるようです。その場合は、-crfオプションで動画の品質を指定すると圧縮後のサイズを調整できます。

    ffmpeg -i input.mp4 -crf 30 -c:v libx264 -c:a aac output.mp4

-crfオプションの値の範囲とデフォルト値はコーデックにより違うようです。小さい値ほど品質は良くなります。

mp4以外も圧縮できる

入力ファイルはmp4以外でも問題ありません。以下はmovを入力してmp4を得る場合。

    ffmpeg -i input.mov -c:v libx264 -c:a aac output.mp4

また、出力ファイルの形式も変更可能であり、指定した出力ファイル名の拡張子を見て自動で切り替えてくれます。

 

組込みソフト開発で学んだ知識まとめ

プロフィールの通り、自分は組込みソフトエンジニアであり、就職してからいつの間にか10年近く経ちました。10年目にしてはあまり自信はないのですが、思い起こせばいろいろと考えたり学んだりしてたので、ここでまとめてみたいと思います。学生の頃にロボット製作をしていたため、マイコンプログラミングは経験していましたが、就職してから34年目くらいまでは結構ついていくのに必死でした。その頃を思い出しながら書きたいと思います。

 

f:id:mzmlab:20190703213900j:plainRepublicaによるPixabayからの画像

 

  

はじめに~組込みソフト開発ってどんなことするの?

組込み系と一口に言っても、システムの規模によって使う技術も様々ですが、自分は主にマイコンF/Wの開発に携わってきました(システムの規模は小さく、OSもなし)。どんなことをしているのかについては、最近たまたま同じような境遇の方のブログを見つけ、とても共感したので、自分で書き直すことはせずリンクを貼らせていただきます(^^)

nmmmk.hatenablog.com

 

組込みソフト開発で学んだ知識

MISRA-C

マイコンF/W開発のチームに入ってまず渡されたのが、MISRA-Cの本でした。

 『組込み開発者におくるMISRA‐C:2004―C言語利用の高信頼化ガイド』
 
 MISRA‐C研究会 日本規格協会

MISRA-Cは自動車業界で生まれた、C言語で安全なプログラムを書くためのコーディング規約です。本を読んでみた感想として、ほとんどのルールがコンパイラの基本的な仕様に関するものであり、自動車業界に限らず知っておいたほうがよいと思える内容でした。

実際、MISRA-Cを学ぶまで知らなかったコンパイラ仕様もあるので、少し紹介します。

・汎整数拡張と平衡化
異なる型の間での演算を行う場合、暗黙的に同じ型に変換されますが、この暗黙の変換は汎整数拡張と平衡化により実行されます。まず汎整数拡張により、式中の整数型(charshort)はint型に昇格します。その後、平衡化により、より精度が高いオペランドlongfloatdouble)に合わせられます。自分は汎整数拡張のことを知らなかったので、今更C言語でこんな発見があるのかと、感心してしまったのを覚えています。

・プロトタイプ宣言がない場合のコンパイル結果
プロトタイプ宣言がなくてもコンパイルは通るため、プロトタイプ宣言が抜けていないかのチェックは疎かになりがちですが、実はプログラムの動作結果に影響します。プロトタイプ宣言がない関数は、コンパイラが勝手に戻り値と引数をint型と解釈します。そのため、もし定義側の戻り値や引数が浮動小数点数だったとしても、動作時は小数点以下が丸められてしまいます。(コンパイルエラーにしてほしいですね。warningは出ますが、warningがいくつも放置されているプロジェクトだと見落としてしまうので。)

(もう少しあった気がしますが、思い出したら書き足します。)

マイコンの仕組み

マイコンには学生の頃から触れていましたが、就職してから新たに学んだことも多いので、挙げていきたいと思います。なお、すべて以下のサイトで気軽に学ぶことができます(会社HPの技術コラムのようですが、かなり丁寧に書かれていて感心してしまいます)。

www.uquest.co.jp

・ヒープとスタック
学生のときは、大きな配列はローカル変数ではなくmallocで動的確保しましょうとか習った気がしますが、ヒープとスタックについて勉強するとその理由がわかります。

・メモリのセクション
PCアプリのプログラミングではプログラムコードやグローバル変数、定数などがメモリのどこに配置されているか、あまり気にしないと思いますが、マイコンはメモリサイズが小さいため、すべて考えて配置する必要があります。 

・キャッシュ
マイコンでキャッシュを使う場合は、キャッシュの有効/無効をメモリ領域ごとに検討する必要があります。また、組込みではDMAなどを行った場合にキャッシュに最新の値が反映されず、不具合の原因になることがあります(コヒーレンシの問題)。

・スタートアップルーチン
main関数が呼ばれる前のマイコンの初期化処理であり、アセンブラで書かれています。大抵はマイコンのベンダーがサンプルを用意しているため、自分でゼロから作ることはありませんが、簡単なカスタマイズは自分でする必要があります。

(もう少しあった気がしますが、思い出したら書き足します。)

デバッガ(ICE

学生のときは、マイコンプログラムのデバッグLEDprintf(出力先はシリアル通信のターミナル)で行っていましたが、仕事ではICEによりIDE上でデバッグすることが多いです。ICEによって、Visual Studioのようにプログラムを1ステップずつ実行したり、メモリダンプしたりできます。また、CPUレジスタペリフェラルレジスタを参照・設定したり、キャッシュの内容を表示したり、スタックトレースできたりと、デバッグが大変はかどります。

また、IDEマイコンCPUやメモリをエミュレートする機能を持っている場合があり、開発初期に実機が入手できないといった状況でもデバッグを行えます(ペリフェラルはさすがにエミュレートできないため、主にアルゴリズムデバッグ単体テストで活躍しました)

科学技術計算

マイコンプログラムは基本的にC言語で開発するため、科学技術計算の標準的なライブラリはありません。そのため、経験不足な自分は、客先から要求されたとき、焦って実装方法を調べることになります(好きな分野なので、そういうのを待ってました\(^o^)/という感じではありますが)。

過去経験したものに、最小二乗法があります。なんとなく原理は知っていましたが、自分で実装したことはなく、実装できたとしても処理速度や計算精度といった性能面で要求を満たせるのかが心配でした(もちろん、インターネットに転がっているプログラムでも同じ心配があります)。そんなとき、たまたま持っていた本が、『C言語による最新アルゴリズム事典(奥村 晴彦 著)』でした。久しぶりに本を開いてみると、最小二乗法について計算効率まで考慮したアルゴリズムソースコード付きで載っており、本を買った時の期待以上に助けられました。もちろん、本に載っているからといっても使うときは自己責任ですが、自作やインターネットに比べたら安心感は段違いです。

高速フーリエ変換FFT)を要求されたこともありますが、これはマイコンのベンダーがライブラリを提供していたため、自分で組まずに済みました(信号処理向けのマイコンだったので、当然といえば当然ですが)。ただ、コンパイルを成功させるまでに時間がかかったり、その後使いこなすまでにも時間がかかったりはします。

難しいアルゴリズムの実装はプレッシャーもありますが、動いた時の喜びも大きいので自分は好きです。