Pythonロボティクスコース レッスン 38

テーマ.10-1 バーコードリーダーの制作

コンピュータ上での数の表し方を学ぼう

このレッスンで学ぶこと

このレッスンでは、2進数や16進数などコンピュータにとって相性の良い数の表し方について学習します。また、学習したことを踏まえて、簡単なバーコードを読み取って購入する商品の精算を行えるレジシステムを制作します。

コンピュータに適した数の表し方

10という数字が表す数は「十」、100という数字がが表す数は「百」と、私たちが普段から使う数の表し方を「10進数」といいます。10進数は、0~9までの10種類の数字で数を表す方法です。人にとって10という区切りは、両手の指の数とも一致しているため、数を数えるときに分かりやすい表現になっていると言えます。

一方でコンピュータにとっては、10進数は非常に扱いにいく数の表し方です。その理由はコンピュータの内部でどのように計算が行われているのかを知ると理解できます。

2. 1 コンピュータの計算方法

コンピュータの計算は、中心部であるCPUで行われます。このCPUは簡単に例えると、スイッチのオン・オフを切り替えるシンプルな回路が、膨大な数集積された回路になっています。

この回路で例えると、スイッチがオフのときは発光ダイオード(LED)の両端にかかる電圧が低い状態に(LOW)にあり、スイッチがオンのときは電圧が高い状態(HIGH)にあります。コンピュータ上では、この電圧が低い状態(LOW)と高い状態(HIGH)を、デジタルの「0」と「1」に置き換えています。そして、「0」と「1」の論理演算を行えるように回路を組むことで、複雑な計算を処理できるようになっています。このように、論理演算用に組まれた回路を「論理回路」といいます。

2. 2 論理回路の種類

論理回路の基本は「AND」「OR」「NOT」の3種類です。それぞれの回路の働きは、Pythonの論理演算子(andornot)と同じです。

■ AND回路

AND回路は、入力Aと入力Bの両方が「1(HIGH)」のときだけ、出力Yが「1(HIGH)」になる回路です。

入力A入力B出力Y
0(LOW)0(LOW)0(LOW)
1(HIGH)0(LOW)0(LOW)
0(LOW)1(HIGH)0(LOW)
1(HIGH)1(HIGH)1(HIGH)

AND回路をアナログな電気回路に例えると、下の図のように2つのスイッチを直列につないだ回路といえます。この回路では、両方のスイッチがオン(1)のときだけ、発行ダイオードが点灯(1)します。

■ OR回路

OR回路は、入力Aと入力Bのどちらか一方でも「1(HIGH)」であれば、出力Yが「1(HIGH)」になる回路です。

入力A入力B出力Y
0(LOW)0(LOW)0(LOW)
1(HIGH)0(LOW)1(HIGH)
0(LOW)1(HIGH)1(HIGH)
1(HIGH)1(HIGH)1(HIGH)

OR回路はアナログな電気回路に例えると、下の図のように2つのスイッチを並列につないだものになります。この回路では、どちらか一方でもスイッチがオン(1)のときに、発行ダイオードが点灯(1)します。

■ NOT回路

NOT回路は入力の逆を出力する回路です。

入力A出力Y
0(LOW)1(HIGH)
1(HIGH)0(LOW)

コンピュータは、ここまでで確認した3つの論理回路の組み合わせで数の計算を行います。しかし、「0(LOW)」と「1(HIGH)」の2つの状態しかない論理回路では、「0~9」の10種類の数字を使う10進数の計算を直接行うことはできません。そこで10進数の代わとして使われているのが「2進数」という数の表し方です。

2. 3 2進数とは

10進数の世界では、「10 + 10」を計算すると、もちろん「20」という結果になります。一方で、これから説明する2進数の世界では、「10 + 10」の計算結果は「100」です。この「100」という結果は、2進数の世界での「四」を表しています。このように表されるのは、2進数が、「0」と「1」の2つの数字だけを使って数を表現するためです。

さきほどの「10 + 10」の計算では、上位の桁どうしを足すと「2」になりますが、2進数では「2」という数字はないため、次の桁に繰り上がり、「100」になります。

■ 2進数と10進数の比較

2進数の各桁の位を10進数で表すと次のようになります。桁が1つ上がと、位の大きさは2倍になっています。

上の表を踏まえて、10進数の「0~9」を2進数に変換すると下の表のようになります。

この表と見比べて先程の2進数の計算式を、10進数の計算式に置き換えてみましょう。計算結果が正しいことがすぐに理解できます。

では、練習として、次の2進数で表した式を計算し、さらにその結果を10進数で表してみましょう。

【 問題 】
【 解答 】

※こちらも参照↓

■ Pythonでの2進数の表し方

Pythonで2進数を表すときは、先頭に0bを付けます。bは「2進」を英語で表した「binary」の頭文字です。

上の実行結果からも分かるように、そのまま書くと10進数で表した数値として扱われるため、ここでは""で囲み、文字列として定義します。

■ 2進数から10進数への変換

文字列を2進数として解釈し、10進数の数値へ変換したいときは、int()関数を使います。

int(文字列, 文字列が何進数で表されているのかを示す数字)
【 サンプルコード 2-3-1 】
追加【2・3行目】
※ 10進数は英語で「decimal number」といいます。
(実行結果)

■ 10進数から2進数への変換

反対に、10進数の数値から2進数の文字列に変換するときは、bin()関数を使います。

bin(10進数の数値)
【 サンプルコード 2-3-2 】
(実行結果)

■ 論理回路での2進数の足し算

1桁の2進数の足し算は、上で説明した「AND」「OR」「NOT」の論理回路を組み合わせた下の回路で行うことができます。この論理回路は「半加算器」と呼ばれています。1桁の2進数「A」と「B」を入力すると、演算結果の1桁目の「S」と桁上がりの「C」が出力されます。

【 半加算器への入力値と出力値の関係 】

|A|B|C|S|
|:—|:—|:—|:—|
|0|0|0|0|
|0|1|0|1|
|1|0|0|1|
|1|1|1|0|

実際に、AとBにそれぞれ「1」を入力したときの、演算の過程は次のようになります。

このように、コンピュータにとって2進数は計算の都合上とても扱いやすい数の表し方です。しかし、数が大きくなると、どうしても数字の並びが長くなってしまうため、人にとっては扱いづらくなります。

そのため、人にもコンピュータにも扱いやすい数の表し方が必要です。そこで、使われているのが次に説明する「16進数」です。

2. 4 16進数とは

16進数では、数字の「0~9」に加えてアルファベットの「a~f」を使って合計16種類の文字で数を表します。4桁の「0」と「1」の数字の並びが「24 = 16」となり、全部で16通りあることから、16進数は4桁の2進数の代わりに使うことができます。

また、10進数と16進数との対応関係は次の表のようになります。

■ Pythonでの16進数の表し方

Pythonで16進数を表すときは、先頭に0xを付けます。

ここでも、16進数で表したことが分かるように、文字列として定義しておきましょう。

※ 16進数は英語で「hexadecimal number」といいます。

■ 16進数から10進数への変換

文字列を16進数として解釈し、10進数の数値へ変換したいときは、2進数のときと同じくint()関数を使います。

【 サンプルコード 2-3-3 】
追加【2・3行目】
(実行結果)

■ 10進数から16進数への変換

反対に10進数の数値から16進数の文字列へ変換するときは、hex()関数を使います。

【 サンプルコード 2-3-4 】
(実行結果)

16進数の他にも、「8進数」が2進数の代わりに使われることもあります。

2. 5 8進数とは

8進数では、「0~7」の8種類の数字で数を表します。「23 = 8」なので、3桁の2進数の代わりとして使えます。

また、10進数と8進数との対応関係は次の表のようになります。

■ Pythonでの8進数の表し方

Pythonで8進数を表すときは、先頭に0oを付けます。

ここでも、8進数で表したことが分かるように、文字列として定義しておきましょう。

※ 8進は英語で「octal」といいます。

■ 8進数から10進数への変換

文字列を8進数として解釈して、10進数の数値へ変換するときは、2進数や16進数のときと同じくint()関数を使います。

【 サンプルコード 2-3-5 】
追加【2・3行目】
(実行結果)

■ 10進数から8進数への変換

反対に10進数の数値から8進数の文字列へ変換するときは、oct()関数を使います。

【 サンプルコード 2-3-6 】
(実行結果)

2. 6 ゲームで進数間の相互変換を練習する

2進数や8進数、16進数から10進数への変換や、その逆の変換について理解を深めたいときは、以下のサンプルゲームで遊んでみましょう。

※ このゲームは次のチャプターの学習には関係ありません。時間がないときは飛ばしましょう。
【 2進数や8進数、16進数から10進数へ変換するゲームのサンプルコード 】

※start(“モード”) で開始。モードはb,o,h

【 10進数から2進数や8進数、16進数へ変換するゲームのサンプルコード 】

バーコードリーダーの組み立て

レッスンの後半では、上で学習した2進数を応用して、簡単なバーコードを読み取るレジシステムの制作に取り組みます。レジシステムで使うバーコードリーダーをブロックで組み立てましょう。

【 組み立てるバーコードリーダー 】

3. 1 組み立てに必要なパーツ

【 パーツ一覧 】
  • Studuino:bit×1
  • ロボット拡張ユニット×1
  • 電池ボックス×1
  • サーボモーター×1
  • 赤外線フォトリフレクタ×1
  • センサー接続コード(3芯30cm)×1
  • ブロック基本四角(グレー)×6
  • ブロック基本四角(赤)×2
  • ブロックハーフA(グレー)×2
  • ブロックハーフB(グレー)×1
  • ブロックハーフB(黒)×1
  • ブロックハーフC(白)×7
  • ブロックハーフD(白)×8
  • ステー×4
  • ギヤ大×1
  • ラック×1
【 アーテックブロックの形状 】

3. 2 組立説明書

以下のリンク先から組立説明書を確認しましょう。

バーコードリーダーの組立説明書

レジシステムのプログラムの作成

プログラムを作成する前に、組み立てたバーコードリーダーでバーコードを読み取る仕組みを確認しましょう。

4. 1 バーコードを読み取る仕組み

私たちが普段スーパーマーケットやコンビニエンスストアで買い物をする商品のパッケージには、必ずバーコードが印刷されています。

バーコードは、その下にある数字の並びを表しています。この数字の並びは、それぞれのメーカーが取得した、製品に対する固有の(世界で唯一の)番号です。お店は商品の価格や在庫数、販売数など様々なデータをこの番号にひもづけて管理しています。

バーコードが開発された当初は、センサーで数字を読み取ることが難しかったため、代わりにスペース(白)とバー(黒)の並びで表す方式が採用されました。組み立てたバーコードリーダーでは、実際の商品に使われているバーコードを読み取るのは難しいため、代わりに用意した単純なバーコードを使います。

【 バーコードの構造 】

レッスンで扱うバーコードは、4桁の2進数を表しています。4つに分かれたブロックが2進数の各桁に対応していて、色が黒の場合は0を、色が白の場合は1をそれぞれ表します。

このバーコードで読み取った4桁の2進数は、10進数へ変換して「0~15」までの番号の代わりに使います。

また、バーコードは紙や商品の包装紙などに印刷して使われますが、このレッスンでは印刷する代わりに、白と黒のブロックを使いましょう。

バーコード代わりの4つのブロックは、組み立てたバーコードリーダーの中央にセットします。サーボモーターで赤外線フォトリフレタを動かし、上位の桁から順番に読み取ります。

4. 2 制作するレジシステムで行う処理

一般的なレジシステムでは、商品に付けられた番号(EANコード)に対して、商品名や価格などの複数の情報がひもづけられて、データが管理されています。そして、機械に備わっているバーコードリーダーで商品のバーコードを読み取ると、自動的にこれらの情報が引き出され、モニターに表示する仕組みになっています。

そこで、これから制作するレジシステムでは、以下の商品データをあらかじめ辞書に登録しておき、バーコードから読み取った番号から商品を検索して、名前と価格を取得します。

【 商品番号と関連付けた商品名と価格のデータ 】
商品番号商品名価格(円)
1にんじん(carrot)300
2たまねぎ(onion)200
3豆腐(tofu)100
4卵(egg)230
5牛乳(milk)240
6リンゴジュース(apple juice)320
7ソーセージ(sausage)430
8ベーコン(bacon)380
9ポテトチップス(potato chips)200
10アイスクリーム(ice cream)150
11チョコレート(chocolate)350
12砂糖(sugar)250
13醤油(soy sauce)500
14米(rice)2000
15ティッシュペーパー(tissue paper)400

4. 3 プログラムの作成手順

プログラムでは、それぞれバーコードリーダーとレジシステムの役割をもつ2つのクラスを作成します。

クラス名役割
BarcodeReaderバーコードリーダーのクラス。赤外線フォトリフレクタを使ってバーコードを読み取り、レジシステムへ読み取った番号を渡します。
Registerレジシステムのクラス。バーコードリーダーから渡された番号で商品を検索して、商品名と価格を表示したり、これまでの合計金額を計算したりします。

そして、各クラスでは次のプロパティとメソッドを定義します。

【 BarcodeReaderクラスのプロパティとメソッド 】
プロパティ名/メソッド名内容
POSITIONSプロパティバーコードの各ブロックを読み取るときのサーボモーターの角度
THRESHOLDプロパティバーコードの白と黒を見分けるための赤外線フォトリフレクタのしきい値
irpプロパティ赤外線フォトリフレクタの制御用オブジェクト
servoプロパティサーボモーターの制御用オブジェクト
__init__()メソッドインスタンス作成時に実行される初期化処理
read_barcode()メソッドバーコードを読み取り、その番号を返す処理
【 Registerクラスのプロパティとメソッド 】
プロパティ名/メソッド名内容
PRODUCTS_DATAプロパティ商品データを格納した辞書
readerプロパティBarcodeReaderクラスのインスタンス
totalプロパティバーコードを読み取った商品の合計金額
__init__()メソッドインスタンス作成時に実行される初期化処理
read()メソッド読み取ったバーコードの商品名と価格を表示して、さらに合計金額(total)に加算する処理
show_total()メソッド合計金額(total)を表示する処理

また、レジシステムの操作は、Studuino:bitのボタンAとボタンBで次の通り行います。

  • ボタンAを押す: バーコードを読み取り、商品名と価格を表示する
  • ボタンBを押す: これまでにバーコードを読み取った商品の合計金額を表示する

そして、これらのボタン関連の処理やBarcodeReaderクラスとRegisterクラスのインスタンスを作成するコードをmain()関数にまとめて実行します。

では、順番にクラスと関数を定義していきましょう。

4. 4 BarcodeReaderクラスの定義

以下の手順に沿ってBarcodeReaderクラスを定義します。

■ バーコード読み取り時のサーボモーターの角度の設定

以下のサンプルコードを実行して、バーコードの各桁を読み取るときの、サーボモーターの角度を調べましょう。

【 サンプルコード 4-4-1 】

上のサンプルコードの実行中は、ボタンAを押すと赤外線フォトリフレクタが左に移動し、ボタンBを押すと右に移動します。また、ターミナルに移動後のサーボモーターの角度が表示されるため、その数字を確認して、各桁を読み取るときのサーボモーターの角度を決めましょう。

新しいプログラムファイルを開き、BarcodeReaderクラスを宣言します。決めた角度を左(上位の桁)から順番にタプルに格納して、POSITIONSプロパティに入れましょう。

■ 白か黒かを判定するための赤外線フォトリフレクタのしきい値の設定

別のプログラムファイルを新たに開き、以下のサンプルコードを実行します。

【 サンプルコード 4-3-2 】

赤外線フォトリフレクタの直下に白と黒のブロックを置き、それぞれターミナルに表示された値を確認しましょう。確認した値から白か黒かを判定するためのしきい値を決定します。

そして、元のプログラムファイルに戻り、決めたしきい値をTHRESHOLDプロパティとして定義します。

追加【3行目】

■ __init__メソッドの作成

次に、__init__()メソッドを定義します。このBarcodeReaderクラスでは、赤外線フォトリフレクタとサーボモーターを制御します。それぞれのパーツを接続した端子名を引数に受け取り、インスタンスを作成しましょう。

追加【2行目、7行目~9行目】

また、バーコードの読み取りは左(上位の桁)から行います。あらかじめ、サーボモーターを動かし、赤外線フォトリフレクタを左に移動させておきましょう。

追加【10行目】

■ read_barcodeメソッドの作成

今度は、バーコードの読み取りを行うread_barcode()メソッドを定義します。

はじめに、上位の桁から順番にブロックの色を判定して、「"0"(黒)」または「"1"(白)」に変換し、4桁の2進数の文字列を作成します。

追加【1行目、14行目~21行目】

これで、4桁の2進数の文字列へ変換できたので、さらにint()関数を使い、10進数の数値へ変換します。そして、この結果を戻り値として呼び出し元に返します。また、次の読み取りを円滑に行うために、赤外線フォトリフレクタを左へ戻しておきましょう。

追加【21行目~23行目】

最後に、読み取り中であることが伝わるように、赤外線フォトリフレクタの値を取得した後に、ブザーから短く音を鳴らすコードを追加します。これでBarcodeReaderクラスの完成です。

追加【4行目、21行目】

4. 5 Registerクラスの定義

次は、Registerクラスを定義していきましょう。

■ 商品データの登録

はじめに、最初に確認した16種類の商品データを辞書にまとめ、PRODUCTS_DATAプロパティとして定義します。商品番号をキーにし、商品名と価格を格納した辞書をひもづけましょう。

追加【28行目~45行目】

■ __init__メソッドの作成

Registerクラスでは、BarcodeReaderクラスの機能を利用してレジの会計の処理を行います。そのため、内部にBarcodeReaderクラスのインスタンスを保有するようにします。__init__()メソッドの引数としてインスタンスを受け取り、readerプロパティに格納しましょう。また、商品の合計金額を記録しておくための変数をtotalプロパティとして定義しておきましょう。

追加【47行目~49行目】

■ readメソッドの作成

BarcodeReaderクラスの機能でバーコードを読み取り、その番号から商品データを検索する処理をread()メソッドとしてまとめます。もしも誤った番号(例えば登録がない「0番」など)が読み取られたときは、PRODUCTS_DATAに存在していないため、エラーになります。そこで、try-except文を用いた例外処理でエラーを適切に処理します。

追加【51行目~57行目】

そして、商品データが取得できた場合は、商品名と価格をターミナルに表示し、合計金額に加算します。

追加【58行目~61行目】

■ show_totalメソッドの作成

このメソッドが実行されると、これまで読み取ったバーコードの商品価格の合計金額(totalプロパティ)をターミナルに表示します。表示後は、合計金額を「0」にリセットしておきましょう。

追加【63行目~66行目】

これで2つのクラスが定義できたました。

4. 6 main関数の定義

最後に、ボタンAやボタンBが押されると、Registerクラスのメソッドを呼び出す処理をつくり、main()関数としてまとめましょう。

追加【2行目、70行目~79行目】

4. 7 動作の確認

完成したプログラムが以下になります。プログラムを実行して、ターミナルからmain()関数を呼び出しましょう。ボタンAを押して、いくつかのバーコードを読み取り、正しく商品名と価格が表示されることを確認しましょう。また、ボタンBを押すと、商品の合計金額が表示されることも確かめましょう。

【 サンプルコード 4-6-1 】

課題:在庫数量の管理機能の追加

高機能なレジシステムには、会計や商品データの管理はもちろん、売上げの管理や会員専用のポイントサービスなど店舗の運営に必要な機能が豊富に備わっています。

この課題では、これらの機能の中から在庫数量に着目して、【 サンプルコード 4-6-1 】を改良します。商品データに新たに在庫数量の情報を追加して、バーコードが読み取られるたびに在庫数量を減らす処理と、残りの数量が少なくなると、在庫不足として商品名をLEDディスプレイに表示して知らせる処理を追加してみましょう。

下の表は、初期の在庫数量と在庫不足を知らせるときの残数量の設定例です。自分で考えたものを設定しても構いません。

【 所為の在庫数量と在庫不足を知らせる残数量の設定例 】
商品番号商品名初期の在庫数量在庫不足を知らせる残数量
1にんじん(carrot)102
2たまねぎ(onion)102
3豆腐(tofu)51
4卵(egg)102
5牛乳(milk)102
6リンゴジュース(apple juice)51
7ソーセージ(sausage)51
8ベーコン(bacon)51
9ポテトチップス(potato chips)51
10アイスクリーム(ice cream)102
11チョコレート(chocolate)51
12砂糖(sugar)51
13醤油(soy sauce)51
14米(rice)51
15ティッシュペーパー(tissue paper)51

5. 1 プログラムの作成手順

上の設定例でプログラムを作成する手順を紹介します。

■ 商品データへの初期の在庫数量と在庫不足を知らせる残数量の追加

商品データを管理しているRegisterクラスのPRODUCTS_DATAプロパティへ新たに初期在庫数量(stock)と在庫不足を知らせる残数量(shortage)の情報を以下のように追加します。

変更【31行目~45行目】

■ 在庫数量を減らして在庫不足になった場合は知らせる処理の追加

在庫数量は、Registerクラスのread()メソッドでバーコードを読み取ったときに減らします。そして、在庫数量が商品データ上の在庫不足を知らせる残数量と一致する場合は、LEDディスプレイに商品名をスクロール表示して知らせます。

追加【30行目、65行目~67行目】

5. 2 動作の確認

以上のように、簡単な変更を行うだけで在庫管理の機能ができました。(※ もちろん実際のレジシステムはこんなに簡単なものではなく、もっと高度な処理が行われています。)それでは、完成したプログラムを実行して動作を確認してみましょう。

【 サンプルコード 5-2-1 】

おわりに

6. 1 このレッスンのまとめ

今回は、コンピュータに適した数の表し方として、新たに「2進数」「8進数」「16進数」を学習しました。また、高度で複雑な計算を行っているコンピュータも、細かく分解していくと、単純な論理回路で構成されていることを紹介しました。

レッスンの後半では、白と黒の2つの色から2進数へ、2進数から10進数へと変換できることを利用して、バーコードリーダーを備えた簡単なレジシステムを制作しました。

このレッスンで学習したことは、どれもコンピュータを理解する上で大切な基礎知識です。しっかりと復習をして、内容を理解してから次のレッスンに臨みましょう。

6. 2 次のレッスンについて

次回のレッスンでは、コンピュータ上でのデータ量の表し方と、文字データをコンピュータ上で扱うための仕組みについて学習します。

TOP