ハードウェア技術者のスキルアップ日誌

某家電メーカーの技術者がスキルアップのために勉強したことを記録するブログです

Python - 引数を渡す方法

仕事でちょっとしたPythonのコードを書くときに、よく引数を渡す方法を調べているので個人的な備忘録のためにまとめておきます。
これをコピペすれば作りやすいはず!

import argparse

def main():
    # parserを作成
    parser = argparse.ArgumentParser()

    #add_argumentで必要な引数を追加していく
    parser.add_argument('arg1', help='引数の説明(なくてもよい)') #必須の引数 引数が足りないとエラーになる
    parser.add_argument('arg2', help='')

    #--をつけるとオプション なくてもよい引数
    parser.add_argument('--arg3', default=0, type=int, help='') #default:引数を指定しなかったときに使用する値
                                                                 #type:型を指定 この場合自動でintに変換する
    #省略形を作成
    parser.add_argument('--arg4', '-a') #-を一つにすると文字1つでオプションを表現 どちらを使ってもよい

    #引数を使用
    args = parser.parse_args()

    #以降、args.引数名で指定した値を使用できる
    print(args.arg1)
    print(args.arg2)
    print(args.arg3)
    print(args.arg4) #省略形のaでは参照できない

if __name__ == '__main__':
    main()

 

実行するとこんな感じ

$ python sample.py a b --arg3 c -a d

a
b
c
d

 

参考サイト

ほとんどこちらのサイトを参考にさせていただきました。ありがとうございます。

qiita.com

ラズパイでPWM制御

ラズパイを使ってハードウェアを制御するシリーズ、第5回目はPWM通信です。
今回はPWM制御でモーターの回転速度を制御してみました。

 

PWMとは?

PWMはPulse Width Modulationの略で、パルス波のデューティー比を変化させて変調する変調方式のことです。デューティー比とはパルス1周期のHigh期間とLow期間の比のことで、モーターの制御においてはデューティー比を変えることで回転速度を制御することができます。

詳細はこちらの記事を参照ください。
https://monoist.atmarkit.co.jp/mn/articles/0706/06/news132.html

 

モーター制御に使用したパーツ

モーターを動かすにはそれなりの電流が必要ですので、ラズパイで直接は駆動できません。このため、モーター駆動用のモータードライバキットと電源として乾電池を使うために電池ボックスを準備しました。
http://akizukidenshi.com/catalog/g/gK-11219/
http://akizukidenshi.com/catalog/g/gP-11523/

モーターはこちらのDCモーターを使用しました。
http://akizukidenshi.com/catalog/g/gP-09169/

実は、モーターの駆動電圧が1.5V-3.0Vということを知らずに単三x4の電池ボックスを購入してしまいました。パーツを購入するときは仕様をよく調べて購入しましょう(笑)

これらのパーツを使って以下のように回路図を組みました。

f:id:masashi_k:20200524230405p:plain

後で述べますが、ラズパイはどのGPIO端子からでもPWM信号を出力できるので、使用する上記の通りにする必要はなく、GPIO端子はどれでもOKです。
配線のしやすさやIIC, SPIなど他機能との兼ね合いでお選びください。

また、モーターが回転していることが分かるように、モーターに100均扇風機のファンを取り付けました。無理やり引っ張って外しました。

ラズパイでPWM信号を出力する方法

ラズパイでPWMを使用するにはI2C制御の時に使用したwiringpiというライブラリを使用します。
http://wiringpi.com/reference/software-pwm-library/

サンプルコードを以下に示します。

import wiringpi

pin=1 #GPIO番号を指定

#初期設定
wiringpi.wiringPiSetupGpio() #GPIO番号で設定
wiringpi.pinMode(pin, wiringpi.OUTPUT)  #GPIO出力設定
softPwmCreate(pin, initialValue, pwmRange)   #PWM設定  Duty比の初期値と最大値を指定

#PWM制御
softPwmWrite(pin, value)                     #Duty何%で動かすかを設定


使用している関数について補足します。

int softPwmCreate (int pin, int initialValue, int pwmRange) ;

どのGPIOピンをPWMとして制御するかを設定します。initialValueとpwmRangeはデューティーの初期値と最大値を意味し、0-100の値を設定します。

void softPwmWrite (int pin, int value) ;

指定したGPIOから出力するデューティー比(value)を設定します。

モーターの回転数を制御するプログラム

今回作成したラズパイを使ってモーターを駆動するpythonプログラムはこちらです。
扇風機の風の強さを弱→中→強と変えた後、停止するようにしています。

import wiringpi as pi
import time

# GPIOピンの定義
AIN1 = 26
AIN2 = 16
PWMA = 19
STBY = 20

# GPIOピンの設定
pi.wiringPiSetupGpio()
pi.pinMode( AIN1, pi.OUTPUT )
pi.pinMode( AIN2, pi.OUTPUT )
pi.pinMode( PWMA, pi.OUTPUT )
pi.pinMode( STBY, pi.OUTPUT )

# PWM端子に接続したGPIOをPWM出力できるようにする
pi.softPwmCreate( PWMA, 0, 100 )

# スタンバイ状態にする
print("StandBy")
pi.digitalWrite( STBY, 0 )
pi.digitalWrite( AIN1, 0 )
pi.digitalWrite( AIN2, 1 )
pi.softPwmWrite( PWMA, 0 )

#SW on
pi.digitalWrite( STBY, 1 )

#Level1
print("Level weak")
pi.softPwmWrite( PWMA, 50 )
time.sleep(3)

#Level2
print("Level middle")
pi.softPwmWrite( PWMA, 75 )
time.sleep(3)

#Level3
print("Level strong")
pi.softPwmWrite( PWMA, 100 )
time.sleep(3)

#終了処理
print("Stop!!")
pi.digitalWrite( STBY, 0 )
pi.digitalWrite( AIN2, 0 )
pi.softPwmWrite( PWMA, 0 )

実際に動かしたときの動画です。

まとめ

ラズパイからPWM信号を出力し、モーターの回転数を制御する回路とプログラムを作成しました。モーターはこれまでの仕事でもあまり扱ってこなかったので、モータードライバーの使い方などいろいろと勉強になりました。

今回はDCモーターを使いましたが、モーターの種類は他にもいろいろあるので、今後は別のモーターも使ってみたいと思っています。次はステッピングモーターを使ってカメラのパン制御をしてみたいですね。

最後まで読んでいただきありがとうございました。

ラズパイでSPI通信を行う

ラズパイを使ってハードウェアを制御するシリーズ、第四回目はSPI通信です。
前回と同じく、加速度センサーをラズパイに接続して動かしてみました。

 

SPIとは?

SPIは Serial Peripheral Interface の略でデバイス同士を接続するシリアルバスの1種です。I2Cと同じく、一つのマスターが同じバスで複数のデバイスと通信ができます。通信速度はI2Cに比べて比較的速いです。信号線はSCK, MISO, MOSI, CSの4本から成ります。

規格の詳細は割愛しますが、こちらに日本語の解説があります。
https://www.analog.com/jp/analog-dialogue/articles/introduction-to-spi-interface.html

 

ラズパイとデバイスの接続

ラズパイでSPIを使用するにはデバイスと以下のように接続します。
ラズパイのSPI端子は設定を変えることでGPIO8をCE0#に, GPIO9をMISOに, GPIO10をMOSIに, GPIO11をSCLKに割り当てることができます。

   f:id:masashi_k:20200506223949p:plain

ちなみに、ラズパイにはCE端子がもう一本用意されており、GPIO7がCE1#となります。

今回もアナログデバイセズの加速度センサーADXL345を使います。同一デバイスでI2CとSPIインターフェースの両方を使用できます。今回はSPIインターフェースで動かしてみます。

購入サイト
http://akizukidenshi.com/catalog/g/gM-06724/
データシート
http://akizukidenshi.com/download/ds/freescale/ADXL345_jp.pdf

センサーモジュールとの接続は以下の通りです。

f:id:masashi_k:20200506225233p:plain

 

SPIを使うための設定

Rasbianのデフォルト設定ではラズパイでSPIを使うことはできません。
GUIの設定メニューからSPIを有効にします。

設定ーRaspberry Piの設定ーインターフェース

f:id:masashi_k:20200503232411p:plain

f:id:masashi_k:20200506225701p:plain

SPIを有効にした後、再起動するとSPIが使用できるようになります。

 

バイスがI2C接続で認識されているかを確認するために以下のコマンドを実行します。spi_bcm2835が表示されればOKです。

$ lsmod | grep spi
# spi_bcm2835 16384 0

 

Pythonライブラリのインストール

ラズパイのpythonでSPI通信を行うにはpi-spidevライブラリを使用します。
pipコマンドでインストールします。

$ sudo apt-get install python-pip
$ sudo pip3 install spidev

 

SPIで通信を行うプログラム

上記でインストールしたspidevライブラリを使って値を読み書きするサンプルコードを以下に示します。

import spidev

read_addr = 0x00 
write_addr = 0x1E 
write_data = 0x08

#初期設定
spi = spidev.SpiDev()
spi.open(0,0)
spi.mode = 3  #このデバイスはSPI mode3で動作
spi.max_speed_hz = 1000000

#アドレス"read_addr "の値を読み出す
read_data = spi.xfer2([0x80 | read_addr,0x00]) 
print('read_data = 0x{:02x}'.format(read_data[1]))  # デバイスID 11100101 が読めるはず
# read_data = 0xe5

#アドレス"write_addr "に対してwrite_dataを書き込む
spi.xfer2([write_addr, write_data])

#正しく書き込めたことを確認
read_data = spi.xfer2([0x80 | write_addr,0x00])
print('write_data = 0x{:02x}'.format(read_data[1]))
# write_data = 0x08


いくつかソースコードについての補足です。
①SPI mode

SPIには4つのモードがあります。ポイントはデータをラッチするタイミングがSCLKの立ち上がりエッジか、立下りエッジがということと、SCLK未送信時の電圧レベルがHighかLowかという2点です。今回使用するADXL345のデータシートのP15(図36-37)を見ると、このデバイスはmode3であることが分かるのでmode3で制御します。

f:id:masashi_k:20200510223623p:plain

(引用:http://www.lapis-semi.com/lazurite-jp/contents/reference/spi.html

②spi.xfer2の仕様

spidevのサイトを見ると以下の記載があります。Chip selectを使った通信方法で引数には送信データのリストを渡します。

xfer2(list of values[, speed_hz, delay_usec, bits_per_word])
Performs an SPI transaction. Chip-select should be held active between blocks.

引数の詳細はADXL345のデータシートのP15(図36-37)を確認します。

まずReadの場合、最初のByteのbit7を1にしておく必要があります。bit6は複数Byte読み出し指定。複数Byteを連続で読み出す場合はここを1にする。それ以降5-0bitは読み出すアドレスを指定します。

2Byte目以降は任意の値でよいのですが、0x00としています。読み出すデータが1Byteであれば0x00を一つ、複数Byteを連続で読み出したい場合はその数だけ0x00を送信します。

読み出したデータは2Byte目以降に入ってくるのでリストの2個目以降を参照します。

f:id:masashi_k:20200510230949p:plain

次にWriteの場合ですが、Readとほぼ同じですが最初のByteのbit7が0となります。bit6はReadの時と同様に複数Byte書き込みビットです。以降の5-0bitは書き込むアドレスを指定します。

2Byte目以降は書き込むデータを転送します。複数Byteを連続して書き込む場合はその数だけデータを送ればOKです。

f:id:masashi_k:20200510231131p:plain

リアルタイムに3軸加速度を読み出すプログラム

SPI通信を使ってリアルアイムにx,y,z方向の加速度を読み出すプログラムを作って動かしてみました。

import spidev
import time

#初期設定
spi = spidev.SpiDev()
spi.open(0,0)
spi.mode = 3  #このデバイスはSPI mode3で動作
spi.max_speed_hz = 1000000

spi.xfer2([0x2D, 0x08]) #測定スタート

try:
    while True:
        #x,y,z方向の加速度を取得(2の補数表現)
        x_data_list = spi.xfer2([0xc0|0x32, 0x00, 0x00])
        y_data_list = spi.xfer2([0xc0|0x34, 0x00, 0x00])
        z_data_list = spi.xfer2([0xc0|0x36, 0x00, 0x00])
        x_data = x_data_list[1] | (x_data_list[2] << 8)
        y_data = y_data_list[1] | (y_data_list[2] << 8)
        z_data = z_data_list[1] | (z_data_list[2] << 8)
        #2の補数を10進に変換
        if(x_data & 0x8000):
            x_data = ((~x_data & 0xFFFF) + 1)*-1
        if(y_data & 0x8000):    
            y_data = ((~y_data & 0xFFFF) + 1)*-1
        if(z_data & 0x8000):
            z_data = ((~z_data & 0xFFFF) + 1)*-1
        #加速度に変換(Dレンジ ±2g)
        x_data = 2 * 9.8 * x_data / 0x7FFF
        y_data = 2 * 9.8 * y_data / 0x7FFF
        z_data = 2 * 9.8 * z_data / 0x7FFF
        print('x: {:4.2f}, y: {:4.2f}, z: {:4.2f} [m/s^2]'.format(x_data, y_data, z_data))
        time.sleep(0.1)
    
except KeyboardInterrupt:
    print('!FINISH!')

実行結果は以下の通りです。I2Cと同様に動かすことができました。

f:id:masashi_k:20200510233444p:plain

まとめ

ラズパイでSPI通信を行う方法をまとめてきました。今回使用したspidevはI2Cのモジュールwiringpiと比較して送受信データのフォーマットを意識する必要があるため少し面倒ですが、慣れれば問題なさそうです。

ここまで、UART, I2C, SPIと主要なデータ通信のインターフェースの使い方を勉強できたので、大抵のデバイスはラズパイに接続して制御できるようになったと思います。次回はPWM制御をやってみたいと思っています。

参考サイト

https://stackoverflow.com/questions/48321492/adxl345-device-id-and-offset-being-wrong-raspberry-pi
https://teratail.com/questions/182635

ラズパイでI2C通信を行う

ラズパイを使ってハードウェアを制御するシリーズ、第三回目はI2C通信です。
今回は実際にデバイスをラズパイに接続して動作確認を行ったので、その手法も合わせてまとめておきたいと思います。

I2Cとは?

I2Cは Inter-Integrated Circuit の略でアイ・スクエアード・シーと読みます。
SCL, SDAの二本の信号線からなるシリアルバスです。

規格の詳細は割愛しますが、こちらに日本語の規格仕様書がアップされています。http://ekousaku.web.fc2.com/doc/I2C.pdf

 

ラズパイとデバイスの接続

ラズパイでI2Cを使用するにはデバイスと以下のように接続します。
I2Cの端子はオープンコレクタなので、外付けのプルアップ抵抗が必要です。センサーモジュールをお使いの場合はそちらに実装されていることがあるので、回路図で確認してください。

ラズパイのI2C端子は設定を変えることでGPIO2をSDAに, GPIO3をSCLに割り当てることができます。

 f:id:masashi_k:20200503223555p:plain

 

今回はアナログデバイセズの加速度センサーADXL345を使って動作確認をしてみます。
(こちらのサイトからモジュールを購入しました)
http://akizukidenshi.com/catalog/g/gM-06724/
データシート
http://akizukidenshi.com/download/ds/freescale/ADXL345_jp.pdf

 

センサーモジュールとの接続は以下の回路図のようにしました。CS端子とVDDを接続するとI2Cモードで動作するようになります。

  f:id:masashi_k:20200503231353p:plain


実際の接続(まだ電源は入れていません)

f:id:masashi_k:20200503231802j:plain



I2Cを使うための設定

Rasbianのデフォルト設定ではラズパイでI2Cを使うことはできません。
GUIの設定メニューからI2Cを有効にします。

設定ーRaspberry Piの設定ーインターフェース

f:id:masashi_k:20200503232411p:plain

f:id:masashi_k:20200503232554p:plain

I2Cを有効にした後、再起動するとI2Cが使用できるようになります。

 

バイスがI2C接続で認識されているかを確認するために以下のコマンドを実行します。

$ i2cdetect -y 1


正しく認識されると以下のように表示されます。0x1dが接続されている加速度センサーのスレーブアドレスです。

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- 1d -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

 

Pythonライブラリのインストール

ラズパイのpythonでI2C通信を行うにはWiringPiライブラリを使用します。
pipコマンドでインストールします。

$ pip3 install wiringpi

 

I2Cで通信を行うプログラム

上記でインストールしたWiringPiライブラリを使って値を読み書きするサンプルコードを以下に示します。

import wiringpi

slv_addr = 0x1D   #デバイスのスレーブアドレス
read_addr = 0x00   #デバイス内でアクセスしたいアドレス
write_addr = 0x1E    #デバイス内でアクセスしたいアドレス
write_data = 0x08   #書き込むデータ値

#初期設定
wiringpi.wiringPiSetup()
i2c = wiringpi.I2C() #get I2C
Fd = i2c.setup(slv_addr) #setup I2C device

#アドレス"read_addr"の値を読み出す
read_data = i2c.readReg8(Fd, read_addr)
print('read_data = 0x{:02x}'.format(read_data))  # デバイスID 11100101 が読めるはず
#>>read_data = 0xe5

#アドレス"write_addr"に対してwrite_dataを書き込む
i2c.writeReg8(Fd, write_addr , write_data)

#正しく書き込めたことを確認
read_data = i2c.readReg8(Fd, write_addr)
print('write_data = 0x{:02x}'.format(read_data)) 
#>>write_data = 0x08

read, writeとも8bit用の関数を使用していますが、16bitの関数も準備されています。

  • int wiringPiI2CWriteReg8 (int fd, int reg, int data) ;
  • int wiringPiI2CWriteReg16 (int fd, int reg, int data) ;
  • int wiringPiI2CReadReg8 (int fd, int reg) ;
  • int wiringPiI2CReadReg16 (int fd, int reg) ;

wiringpiのサイト
http://wiringpi.com/reference/i2c-library/

 

リアルタイムに3軸加速度を読み出すプログラム

i2cのアクセス関数を用いてリアルアイムにx,y,z方向の加速度を読み出すプログラムを作りましたので、参考までに乗せておきたいと思います。

import wiringpi
import time

slv_addr = 0x1D

#初期設定
wiringpi.wiringPiSetup()
i2c = wiringpi.I2C() #get I2C
Fd = i2c.setup(slv_addr) #setup I2C device

i2c.writeReg8(Fd, 0x2D , 0x08) #測定スタート

try:
    while True:
#x,y,z方向の加速度を取得(2の補数表現) x_data = i2c.readReg16(Fd, 0x32) y_data = i2c.readReg16(Fd, 0x34) z_data = i2c.readReg16(Fd, 0x36) #2の補数を10進に変換 if(x_data & 0x8000): x_data = ((~x_data & 0xFFFF) + 1)*-1 if(y_data & 0x8000): y_data = ((~y_data & 0xFFFF) + 1)*-1 if(z_data & 0x8000): z_data = ((~z_data & 0xFFFF) + 1)*-1
#加速度に変換(Dレンジ ±2g) x_data = 2 * 9.8 * x_data / 0x7FFF y_data = 2 * 9.8 * y_data / 0x7FFF z_data = 2 * 9.8 * z_data / 0x7FFF print('x: {:2.2f}, y: {:2.2f}, z: {:2.2f} [m/s^2]'.format(x_data, y_data, z_data)) time.sleep(0.1) except KeyboardInterrupt: print('!FINISH!')

実行結果は以下の通りです。センサーを動かすと加速度が変化することが確認できました。

f:id:masashi_k:20200505011606p:plain

 

まとめ

ラズパイでデバイスとI2C通信を行う方法を記載しました。加速度への変換部分がこれでよいのか自信がないですが、I2Cの読み書きは問題ないはず。
せっかく加速度センサを購入したので、何か面白い使い道がないか考えてみたいと思います。

 

Keras/Tensorflow - h5/hdf5からckptへの変換方法

以前、ckptからpbへの変換方法をブログに書きましたが、今回はKerasのh5/hdf5形式からckptへ変換する方法を調べましたので、ここにメモしておきます。

masaeng.hatenablog.com

 

変換方法

私の環境でうまくいったサンプルコードです。

from keras import backend as K
from keras.models import load_model
import tensorflow as tf

K.clear_session()
K.set_learning_phase(0)

model = keras.models.load_model("xxx.h5")
sess = keras.backend.get_session()

saver = tf.train.Saver()
save_path = saver.save(sess, "xxxx.ckpt")

上のリンクのckptからpbへ変換する方法と組み合わせるとh5/hdf5形式からpb形式に変換することができます。

また、こちらの手法では直接h5/hdf5からpbに変換することができます。
1回で済むのでこちらの方が手軽かもしれません。

from keras import backend as K
from keras.models import load_model
import tensorflow as tf

def freeze_session(session, keep_var_names=None, output_names=None, clear_devices=True):

    from tensorflow.python.framework.graph_util import convert_variables_to_constants
    graph = session.graph
    with graph.as_default():
        freeze_var_names = list(set(v.op.name for v in tf.global_variables()).difference(keep_var_names or []))
        output_names = output_names or []
        output_names += [v.op.name for v in tf.global_variables()]
        # Graph -> GraphDef ProtoBuf
        input_graph_def = graph.as_graph_def()
        if clear_devices:
            for node in input_graph_def.node:
                node.device = ""
        frozen_graph = convert_variables_to_constants(session, input_graph_def,
                                                    output_names, freeze_var_names)
        return frozen_graph


def main():
    model = load_model('xxx.h5')
    frozen_graph = freeze_session(K.get_session(),
                            output_names=[out.op.name for out in model.outputs])

    # Save pb file
    tf.train.write_graph(frozen_graph, "SAVE_PATH", "xxxx.pb", as_text=False)

if __name__ == '__main__':
    main()

 

参考サイト

https://stackoverflow.com/questions/52650842/how-to-convert-hdf5-to-tensorflow-checkpoint

https://www.dlology.com/blog/how-to-convert-trained-keras-model-to-tensorflow-and-make-prediction/

5Gの規格を勉強して分かった、理解する上での前提知識と誤解していたこと

仕事で5Gを扱うことになり、いろいろな文献を読み漁って5Gの規格を調査しました。これだけ話題となっている技術で、すでにサービスが開始されようとしている段階で今更感があるのですが。。。

前置き:どのようにして調査したか

初めは英語の規格書にチャレンジするも、膨大すぎてどこが自分に関係があるところかすらよくわからない。

日本語のサイトを探してもニュース記事程度の簡単なものばかりで、技術的な詳細な解説は少ない。5Gはこれまでの移動通信システム規格と異なり、キャリヤや基地局、携帯端末メーカー以外の幅広い分野での活用が期待されているため、専門家でなくても理解できるように優しく書かれている記事が多い印象です。ですが、端末を開発するのは全く不十分。

docomoがジャーナルにて技術解説を公開してくれていますが、これまでの規格を知っていることが前提で書かれています。例えば、4Gからどう変わったかという差分の説明がメインです。今まで移動通信に携わっていない本当の初心者がこの説明だけを読んで理解するのはほぼ不可能だと思います。

www.nttdocomo.co.jp

今回、以下の文献をメインに4Gの規格に遡って勉強しました。

インプレス標準教科書シリーズ 5G教科書 ―LTE/ IoTから5Gまで―

インプレス標準教科書シリーズ 5G教科書 ―LTE/ IoTから5Gまで―

 

規格の調査を通して、5Gの規格を理解する上で、ある程度の前提知識が必要だと感じました。無線端末や基地局を扱う方にとっては当然の内容ですが、前述したようなニュース記事ではあまり触れられていないことをまとめておきたいと思います。

また、5Gについてよく言われていることで、個人的に誤解していたこともいくつかありました。これらについて正しくはどうなのかを書いていきたいと思います。

 

5Gの規格を理解するための前提知識

3GPP, ITU-R

5Gの規格を調べていると3GPPITU-Rという単語がよく出てきます。ともに規格団体のようで、それぞれで5Gの規格をリリースするスケジュールになっており、関係性がわかりませんでした。

図3 3GPPリリース15と2019年末のリリース16のロードマップ 引用:https://sgforum.impress.co.jp/article/4880

Wikipediaで調べると、3GPPは移動通信システムの標準化作業を行うプロジェクト、ITU-Rは電気通信分野における国際連合の専門機関である国際電気通信連合(ITU: International Telecommunication Union)の無線通信部門とのことです。

ITU-Rは各国の代表が集まった国際機関であるのに対し、3GPPは企業が集まった団体です。移動通信の国際規格はITU-Rで決定するのですが、ITU-Rは自ら規格を作成せず、世界から案を募集しています。3GPPはこの提案書を作成する目的で結成されています。

ですのでスケジュールとしては3GPPが仕様案をリリースし、それをITU-Rが確認して仕様策定という流れとなります。正式仕様はITU-Rがリリースするものとなりますが、技術者としては3GPPの動きを注目する必要がありますね。

3GPPで検討された規格はリリース○○という形でリリースされます。リリース15以降が5Gの規格に相当します。一方、ITU-Rで策定する5G相当の規格はIMT-2020という名称です。

 参考サイト
https://sgforum.impress.co.jp/article/4880
http://www.rf-world.jp/bn/RFW37/samples/p079-080.pdf

 

3GPPのリリースと移動通信規格の世代との関係

3GPPは1,2年おきにバージョンアップした機能をリリースしています。一方で、よく耳にする"3G", "4G"という呼び方は移動通信の機能が大きく変わるタイミングで切り替わっており、3GPPの複数のリリースを指しています。具体的には以下のようになります。

f:id:masashi_k:20200321000538p:plain


③リリース15から5Gの規格を規定

②でも少し触れましたが、3GPPのリリース15から5Gの規格が規定されています。このリリース15には従来のLTE/LTE-Advancedと互換性がある高度化LTEと、これまでとは全く異なる周波数帯を用いる新たな無線技術New Radio(NR)の大きく2つの規格が含まれています。NRは従来のLTE/LTE-Advancedとは互換性がありません。

通常、"5G"と言うときに指しているのはNew Radioの技術だと思いますが、正式には高度化LTEも含めてリリース15以降で規定されている移動通信システムを5Gと呼びます。


引用:https://www.edn.com/what-is-5g-nr/

 

移動体通信システムの構成

移動体通信システムは端末と基地局が電波で接続されており、さらに交換局を通してインターネットや別の端末に接続されるという構成から成っています。

「携帯電話 基地局 インターネット」の画像検索結果引用:https://time-space.kddi.com/au-kddi/20190514/2627

今となっては当然のことですが、当時はこの構成が頭に入ってなく、この構成を意識することで文献の内容がよく理解できるようになったことがあります。

例えば、スタンドアローン(SA)方式、ノンスタンドアローン(NSA)方式。
基地局、交換局(コアネットワーク)は4G, 5G(NR)で別のものが必要となるので、早期にサービスを提供するために4Gの設備を流用して運用する方式(NSA)が取られます。


引用:https://www.soumu.go.jp/main_content/000593247.pdf

 

5Gについて誤解していたこと

①導入スケジュール

 5Gの3つの特徴は「超高速・大容量」、「低遅延」、「同時多接続」と言われていますが、5Gが導入されてからすぐにこれらすべての特徴が利用できるわけではありません。ニュース記事ではよくこれらが同列で扱われているため、誤解を招いていると思っています。

5Gの検討スケジュールは以下のように2ステップとなっています。
 フェーズ1:”超高速・大容量”を中心に2020年に実現する5Gの基本仕様の策定
 フェーズ2:2020年以降の実現を念頭に、”低遅延”、”多接続”を含む5Gのフルスペックの策定

5Gのロードマップ。2018年6月に、5Gのフェーズ1の仕様策定が完了する見込みだ(クリックで拡大) 出典:IHS Markit Technology引用:IHS Markit Technology

 

上図ではリリース16は19年12月に策定完了となっていますが、実際は3ヶ月遅れて2020年3月に完了予定です。

20年3月にdocomoが5Gのサービスをスタートするそうですが、そのサービスは「超高速・大容量」ということですね。「低遅延」、「同時多接続」が利用できるようになるのは2022年以降と言われています。これは5Gの導入当初は、先ほど述べたノンスタンドアローン方式でサービスが提供されることに起因します。5Gのフルスペックを実現するには交換局(コアネットワーク)も5G用のものにする必要があります。

 

②ローカル5G

5Gに関するニュース記事ではローカル5Gというワードもよく目にします。当初このワードを見たときに、LoRaのように自営で基地局を設置し、専用のネットワークを構築できると思ってしまいました。

しかし、ローカル5GはLoRaと異なり、基地局を立てて自営網を構築するにも無線局免許を取得する必要があります。LoRaはアンライセンスバンドを使用するため、免許不要でしたが、5Gはそうではないため、自営網だとしても免許が必要ということですね。

また、ローカル5Gの導入メリットについても、誤解していた点がありました。LoRaの場合、自拠点内に端末を多数設置する場合、端末1台ごとにサービス利用料を払うよりは自前で通信網を作る方がランニングコストがなくなるため、通信コストを削減できるというメリットがあったと思います。

しかし、ローカル5Gに関しては別のところに導入する意義があります。

5Gのスケジュールのところに記載しましたが、5Gのフルサービスが提供されるのは2022年以降となる見込みです。また、キャリアが設備を設置するのは利用者が多い都市部が最初で、人口が少ない地方は導入がさらに後になる可能性が高いです。

一方、産業界において、低遅延など5Gの特徴を生かしたい工場や建設現場などは地方にあることが多く、キャリアによる5Gのサービス導入を待つと非常に時間がかかってしまします。サービスを利用したい企業自らが自拠点に通信網を早期に構築し、5Gの特徴を生かしてビジネスを展開できる方法がローカル5Gです。

 

まとめ

5Gの規格を勉強する中で、前提知識として知っておけば理解が速いと思ったこと、勉強前に誤解をしてしまっていたことをまとめてきました。

移動体無線通信の初心者が1から規格を勉強するのはなかなかハードルが高かったですが、同様のことをされる方がこの記事によって少しでもスムーズに規格を習得できればと思います。

 

経験値ゼロのハードウェアエンジニアがAIを理解して動かすことができるようになるまで

前回の更新からだいぶ間が開いてしまいましたが、また勉強したことのアウトプットを再開していきます。

今回は今までとはちょっとテイストが変わりますが、表題の通り、AIに関して何も知らなかった私がどのようにAIを勉強して理解できるようになったかを書いていきたいと思います。これからAIやDeep Leaningを始めたいという方の参考になれば幸いです。

背景知識

まず、AIを始める前にやっていたことをご紹介します。

職業は家電メーカーのハードウェアエンジニア。主な担当は電気回路設計やデジタル信号処理。ソフトウェアは大学の時に授業でC言語を習った程度で、実務で使ったことはほとんどなし。

上司からの無茶ぶり

約2年前、突然、当時の上司から「うちの商品にAIを乗せたい。どうすれば作れるか調べて動くものを作れ」という無茶ぶりを受ける。当時開発部隊の中にAIが分かる人は誰もおらず、どこから手をつければいいかも分からない状態。

AIの勉強をとりあえず始める

①通信講座を受ける

会社の研修システムを調べたところ、e-LearningでDeep Leaningの講座が受けられることが分かり、まず手始めに受けてみることに。内容は大学の先生が機械学習からDeep learningまで基本的な内容を講義されているものでした。一方的に講義を聴くだけで演習はなし。

ついていくのに苦労したが、結果としてDeep Leaningの概論を効率的に学ぶことができた。後から考えると、この講座によって系統立ててDeep Leaningを理解でき、このような講座を会社経由で受けられたのは幸運だったと思います。

 

②画像処理を勉強

今回、AIでやりたかったことは画像認識です。しかし、当時画像処理に関してほとんど何も知らなかったので、テキストを購入して画像処理の勉強を開始。

結構ボリュームがある本ですが、同僚と一緒に毎週勉強会をして何とか完遂。Pythonで画像を扱う際、画像処理の知識が大いに役に立ちました。やってて損はないです。

ディジタル画像処理[改訂新版]

ディジタル画像処理[改訂新版]

 

 

OpenCVの講座を受講

画像処理を勉強していた時に友人にOpenCVというソフトウェアライブラリがあることを初めて知る。(恥ずかしながら知りませんでした。。。)

②で勉強したことを実際に試すのによいと思い、①の講座と同様に会社経由で外部セミナーを受講。期間は1日、演習もほとんどなく座学ばかりだったが、OpenCVでどんなことができるのか概要は理解できた。会社でいろいろOpenCVを試したいと受講後は思ったが、結局この時はやらずじまい。

面倒な環境構築をセミナー内で済ますことができたのはよかったです。

 

演習ができる環境を探す

④「ゼロから作るDeep Leaning」を読む

 通信講座で何となくDeep Leaningがどういうものかはわかったのですが、どうやって作ったらいいか見当もつかないという状態。通信講座には演習がないのが原因だと思い、自分で手を動かせるものを求めてこちらの本をやってみました。

いろんなところで紹介されていますが、はっきり言って名著です。

学習、推論それぞれで具体的にどのような計算をしているか数学的な計算とPythonでの実装が記されていて、講座で学んだことの理解がさらに深まりました。

一方、演習を求めて始めたという点では物足りない内容。ネットから本に書かれているソースコードをダウンロードでき、自分のPCで動かすことが可能。しかし、与えられたものをPythonで実行するだけという感じで、やり終えても自分で作れる感じは全くなし。

また、この本で初めてPythonを使う。Pythonに関しては必要なところだけ説明されているだけなので、読後感は「Python何となくわかった気がする」という感じ。(実際は全くわかってない!)

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

  • 作者:斎藤 康毅
  • 発売日: 2016/09/24
  • メディア: 単行本(ソフトカバー)
 

 

Aidemyの講座を受ける

このあたりからブログを始めて勉強した内容をアップすることにしたので詳細は記事を見ていただければと思います。

広告で見かけたAidemyというAIの学習サービスの「コードを書きながら学べる」という所に惹かれて、手始めに無料講座を受けてみる。

aidemy.net

動画で学んだことを使ってコードを入力するという形式だったが、問題が穴埋めだったり、形式的なもので、やはり自分でできる気はせず。すぐに次を探し始める。

 

⑥からあげさんのnoteを購入

いろいろ調べた結果、からあげさんのこちらのnoteに行きつきました。
自分で画像を集めて学習し、その結果を評価するという一連の流れの手法を知ることができるということで、思い切って購入しました。価格は500円。

note.com

このチュートリアルをなぞることで実際のAIモデル開発でどのようなことをするかイメージできるようになりました。また、このチュートリアルを応用して、自分の「カメラ」を認識するモデルを作ってみたりしました。

Githubで公開されているモデルを動かす

YOLO, SSDなど有名なDeep learningのモデルを動かしてみた

もう少し自分でコードをいじるために、Githubで公開されているものを動かしてみることに。有名どころのモデルはすでに日本語のマニュアルを作ってくださっている方がいるので、とっつきやすいです。それに従って画像の推論をやってみました。

マニュアル通りにやるだけでは面白くないので、こうしたらどうなるだろう?ということがいろいろ浮かんでくる。しかし、Pythonのコードがスムーズに読めない。「ゼロから作るDeep Leaning」の範囲内の知識では太刀打ちできないことを知る。

 

Pythonを体系立てて勉強

「みんなのPython」を購入して1冊通して勉強しました。

読み終えたころにはオブジェクト指向が何となくわかり、Githubのコードも読めるように!また、Pythonに関して困ったことがあってもネットで調べれば自力で大体解決できるようになった。

みんなのPython 第4版

みんなのPython 第4版

  • 作者:柴田 淳
  • 発売日: 2016/12/22
  • メディア: 単行本
 

 

Githubのモデルを動かす

⑦で行き詰ったことに再度チャレンジ。いろんなモデルに対して、USBカメラで取得した映像をモデルに入力してリアルタイムに推論するコードを自作しました。

画像の前処理、後処理にOpenCVを使うことが多く、うまくいかない時に試行錯誤することでOpenCVのことも大体使えるように。

からあげさんのnoteで動かしたフレームワークがTensorflow/Kerasだったので、Tensorflow/Kerasのモデルを探して、異なるモデルで同じことを繰り返しました。これにより、フレームワークの基本の型のようなものも習得できた。

全て公開されている学習済みモデルを使っているので、学習プロセスは経験できないものの、Deep Leaningの推論に関してはある程度の経験値を得られたと思います。

 

習得したものを社内で共有

平行して進めたことも含めて、ここまで約一年でDeep Leaningの理論を習得して、実際に動かすことができるようになりました。習得したものをまとめて、社内の関係者に勉強会という形で発表しました。人に説明するには、あいまいなところがあっていはいけないので、文献を読み直すなどして知識を整理することが必要になります。この発表により、さらに自分の理解を深めることができました。

結局、人に教えることが一番自分の勉強になります。

 

まとめ

上司に無茶ぶりをされたことをきっかけに、Deep Leaningを習得するために取り組んできたことを書いてきました。今から約2年前のことですが、振り返ってみると約1年間でかなりのスキルアップができたのだと感じます。

改めてポイントだったと感じることは
・学問的知識、理論を知っておくことは重要
 いきなり演習にチャレンジするよりまず理論を勉強するほうが近道
・実際にコードを触って動かすことが一番理解が深まる
・社内に教えてもらえる人がいなかったので、知り合いのつてを辿ったり、有料研修を利用
・理解したことを説明できればさらに定着する

 

私もDeep Leaningの技術者としてはまだまだで、できることは非常に限定的です。今も仕事やプライベートで勉強を重ねている身ですが、この記事がこれからDeep Leaningの勉強を始める方の一助となれば幸いです。