fc2ブログ

M5Stack Core2とGPSユニットで遊ぶ

 今年の1月のブログでM5StickCとGPSユニットの組み合わせで遊んだことをお話ししました。その時はデータが保存できずに、休日散歩の要所要所で携帯カメラで撮影して値を記録していました。格好悪いですね・・。

 今回はSDカードが装備されているM5Stack Core2とGPSユニットの組み合わせで遊んでみました。作成したArduinoのコードは以下の通りで、前回のM5Stickのものを参考にしました。
#include <M5Core2.h>
#include <TinyGPS++.h>

HardwareSerial GPSRaw(2);
TinyGPSPlus gps;

int cnt=0;
File f;
char lat_v[11];
char lng_v[11];
char alt_v[8];

void setup() {
M5.begin();
M5.Lcd.setRotation(3);
GPSRaw.begin(9600, SERIAL_8N1, 33, 32);
}

void loop() {
M5.Lcd.setCursor(0, 0, 2);
M5.Lcd.fillScreen(BLACK);
M5.Lcd.printf("### GPS TEST %d\n", cnt++);
while(GPSRaw.available()>0) {
if(gps.encode(GPSRaw.read())) {
break;
}
}
if(gps.location.isValid()) {
f = SD.open("/gps_data.txt", FILE_APPEND);
M5.Lcd.printf("LAT:%.6f\n", gps.location.lat() );
M5.Lcd.printf("LNG:%.6f\n", gps.location.lng() );
M5.Lcd.printf("ALT:%.2f\n", gps.altitude.meters());

dtostrf(gps.location.lat(), 10, 6, lat_v);
dtostrf(gps.location.lng(), 10, 6, lng_v);
dtostrf(gps.altitude.meters(), 7, 2, alt_v);

String dataString = "";
dataString += String(cnt);
dataString += String(",");
dataString += String(lat_v);
dataString += String(",");
dataString += String(lng_v);
dataString += String(",");
dataString += String(alt_v);

f.println(dataString);
f.close();

} else {
M5.Lcd.printf("INVALID\n");
}
delay(10000);
}
追記した部分はSDカードの部分で、29行目でSDカードにgps_data.txtというファイルを作り、データを書き込みました。34〜36行目で測定した緯度、経度、高度の数値データを文字列に変換し、38〜47行目で書き込みます。測定周期は10秒にしました。

得られた緯度、経度のデータから距離を算出したいと思い、ネットで情報を探していると「ヒュベニ(Hubeny)の公式」というもので算出できることが分かり、Pythonのコードもありましたので、ライブラリ(distance.py)として読み込ませてもらい、緯度、経度から連続する2点間の距離を求め、それらを合計してトータルの移動距離を求めることにしました。コードは以下の通りです。
import folium
import pandas as pd
import matplotlib.pyplot as plt
import distance

positions = pd.read_csv('gps_data.csv')

map = folium.Map(location=[35.xxxxxx,135.xxxxxx], zoom_start=16)
for i, r in positions.iterrows():
folium.Marker(location=[r['latitude'], r['longtude']], popup=r['position']).add_to(map)
map.save("map_position.html")

# distance calculation
dst = []
dst_sum = []
dst_sum_temp = 0
for i in range(0, len(positions['position'])-2):
start_point = distance.Coordinate(positions['latitude'][i], positions['longtude'][i], positions['altitude'][i])
end_point = distance.Coordinate(positions['latitude'][i+1], positions['longtude'][i+1], positions['altitude'][i+1])
dst_temp = distance.Coordinate.distance(start_point, end_point)
dst_sum_temp += dst_temp
dst.append(dst_temp)
dst_sum.append(dst_sum_temp)

# Total distance plot
plt.xlabel('Time [*10 sec]')
plt.ylabel('Total distance[m]')
plt.plot(dst_sum)
plt.show()

# Altitude plot
plt.xlabel('Time [*10 sec]')
plt.ylabel('Altitude [m]')
plt.plot(positions['position'], positions['altitude'])
plt.show()
今回は私の通勤時に計測を行いました。11行目で地図に得られた緯度・経度の位置をプロットしますが、問題なく目的の位置を表していました。移動距離のプロットは以下の通りです。Total_Distance_210522.png通勤距離約40kmで4,200sec(70min)です。自宅から駅まで自動車で移動し、10分程度駅で電車を待ち、30分電車に乗り、その後徒歩で会社へ。時間軸もほぼピッタリです。気になるのは、実際は電車に乗っていて移動しているのですが、データが同じ値で更新されず、Total distanceが変わっていない箇所がある点です。原因はよく分かりませんが、数分後には最新データに更新されていますので、取り急ぎはスルーしようと思います。
 高度のグラフはこんな感じです。Altitude_210522.png自宅は130mぐらいで、会社近辺は30mの標高であることが分かりました。

 SDカードにGPSのデータを保存できたことで、いろいろ面白いことが分かりました。何かGPSを使ったアイデア(悪い用途ではない)があればボチボチ考えていこうと思います。
スポンサーサイト



PyOCR(Tesseract)を使う

 以前のブログでFreeのOCRソフトのTesseractがAzureのOCRと遜色ない結果だったことをお話しました。その後、何度かTesseractを触っていますが、誤認識をゼロにすることができず、どのようにすれば改善できるか、思案していました。

 その中でフォントが小さいと文字が潰れて誤認識しやすく、逆にフォントが大きいと誤認識が減るのではないかというネットで検索すればありがちな仮説を立てました。やはり、自分で試し、確認しないことには納得行かないので(その点は頑固?)、早速試してみました。

 Tesseractのバージョンは4.1.1でTrainデータはjpn.traineddataとjpn_vert.traineddataの2個を実装済みです。
 サンプル画像は以前のブログの文章で、Chromeで表示させた時の50、67、75、80、90、100、110、125、150、175、200%の11種類を、条件(tesseract_layoutの値)を変更させて実行。文字検出結果の誤認識の数をカウントしました。Input_image_210509.png検証に使ったコードは以下の通り(例はtesseract_layout=6の時)です。
from PIL import Image
import sys
import re
import pyocr
import pyocr.builders

tools = pyocr.get_available_tools()
if len(tools) == 0:
print("No OCR tool found")
sys.exit(1)

tool = tools[0]
print("Will use tool '%s'" % (tool.get_name()))

imglist = [50, 67, 75, 80, 90, 100, 110, 125, 150, 175, 200]
for i in range(0, len(imglist)):
filename = './pic' + str(imglist[i]) + '.png'
txt = tool.image_to_string(
Image.open(filename),
lang="jpn",
builder=pyocr.builders.TextBuilder(tesseract_layout=6)
)

# result output
txt = re.sub('([あ-んア-ン一-龥ー、。]) +((?=[あ-んア-ン一-龥ー、。]))',
r'\1\2', txt)
print( txt )

# file output
with open('res6.txt', mode='a') as f:
f.write(filename)
f.write('\n\n')
f.write(txt)
f.write('\n\n')

結果は以下の表の通りです。OCR_result_210509.pngまともな日本語の文章が検出できたのはtesseract_layoutが1、3、4、6の4条件のみで、0、2の時はプログラムエラーが発生(原因調査までは未実施)。5、7、8、9、10は意味不明の文字が検出されました。

tesseract_layoutの値は以下の内容ですが、詳細は勉強不足で理解できていません。
 0 = 方向およびスクリプト検出(OSD)のみ。
 1 = OSDによる自動ページセグメンテーション。
 2 = 自動ページセグメンテーション、ただしOSDなし、またはOCR
 3 = 完全自動ページセグメンテーション、ただしOSDなし。(デフォルト)
 4 = 可変サイズのテキストの単一列を想定します。
 5 = 垂直に配置されたテキストの単一の均一なブロックを想定します。
 6 = 単一の均一なテキストブロックを想定します。
 7 = 画像を単一のテキスト行として扱います。
 8 = 画像を1つの単語として扱います。
 9 = 画像を円の中の1つの単語として扱います。
 10 = 画像を単一の文字として扱います。

ちなみにtesseract_layout=6の条件の50%と125%の時の結果を抜粋すると以下の通りです。
50%
Raspberry Pi Pico で遊ぶ (1)

un 半生 or いつ wa bi 和音 ee 六 8 半
作出るりて介しているという押導りな才拓がなされていますね。自分きえ良ければいとい
う生沿るさえですね。科衝も理婦で、外出している人そろえをクロースアップして、放で不
本をの外出をもせすに間欄している所はフォーカスきされていない (ニュースとしては画生みがない
てナダ一カ汗的な科衝でマ O ゴミと計われる所だと省います。ちっと、提はこれ
でるにしていいあくまでも人人る見そすい下のストレスが

125%
Raspberry Pi Pico で遊ぶ (1)

コロナ褐で Stay Home な暮らしを送っています。ニュースを見ると都心部から郊外の観光地に
人が出かけて集中しているという間抜けな報道がなされていますね。自分さえ良ければ良いとい
う短絡的な考えですね。報道も報道で、外出している人 々 のみをクローズアップして、在宅で不
要不急の外出をせずに我慢している所はフォーカスされていない ( ニュースとしては面白みがない
ですが ・・)。一方通行的な報道でマ 〇 ゴミと言われる所以だと思います。おっと、悪態はこれ
ぐらいにして ・・。あくまでも個人の意見です ・・。我慢のストレスが ・・。
誤認識の代表格は
 コロナ禍 → コロナ褐、コロナ宰、コロナ礼
 I2C → |I2C、|2C
でした。

今回の評価結果から分かったことは、以下の通り。
 1. 文字のフォントサイズが小さい(50%、8*8 pixels)場合、誤認識が多い。
 2. 文字のフォントサイズが大きくなるにつれて誤認識が減少する傾向がありそうだが、特定倍率(例えば125%)で誤認識が増加することもあった。原因は不明。
 3. ネット情報と同様にtesseract_layoutの値が6の時が、文字のフォントサイズによらず全体的に安定して文字認識できている。

今後、PyOCR(Tesseract)を利用する際は、文字フォントを小さ過ぎず、tesseract_layout=6でn増し確認していこうと思います。

Raspberry Pi Picoで遊ぶ(2)

 前回の続きです。今回はRaspi Picoの温度センサのデータをSDカードに保存する機能を追加しましたので、その備忘録です。
 SDカードのモジュールは以前購入して使われずに転がっていたAE-MICRO-SD-DIPを利用しました。前回のLCD表示もそのまま残し、10秒ごとに温度センサデータを収集して、SDカードに書き込むようにしました。また、データ収集開始からSDカードへのデータ書き込み完了まで、基板上のLEDを点灯させるようにしました。
 回路図は以下の通りです。SDカードモジュールとはSPI通信をします。RaspiPico_LCD_SDC_curcuit_210505.pngSDカードとのSPI通信のプログラム(クラスライブラリ)もGitHub上にあったので、それを利用させてもらいました。ありがとうございます。

 最終的に動かしたコードは以下の通りです。先人の書かれたコードの「つぎはぎ」になっていますが・・。
# Temperature Sensor data logging for Raspberry Pi Pico
import utime

# LCD class library
class ST7032():

def __init__(self, i2c, addr=0x3e):
self.i2c = i2c
self.addr = addr
self.buf = bytearray(2)
self.initDisplay()

def writeCmd(self, cmd):
self.buf[0] = 0x00
self.buf[1] = cmd
self.i2c.writeto(self.addr, self.buf)

def writeData(self, char):
self.buf[0] = 0x40
self.buf[1] = char
self.i2c.writeto(self.addr, self.buf)

def initDisplay(self):
self.i2c.writeto(self.addr, b'\x00\x38')
self.i2c.writeto(self.addr, b'\x00\x39')
self.i2c.writeto(self.addr, b'\x00\x14')
self.i2c.writeto(self.addr, b'\x00\x73')
self.i2c.writeto(self.addr, b'\x00\x56')
self.i2c.writeto(self.addr, b'\x00\x6c')
self.i2c.writeto(self.addr, b'\x00\x38')
self.i2c.writeto(self.addr, b'\x00\x0C')
self.i2c.writeto(self.addr, b'\x00\x01')

def clear(self):
self.writeCmd(0x01)
utime.sleep(0.01)
self.writeCmd(0x02)
utime.sleep(0.01)

def home(self):
self.writeCmd(0x02)
utime.sleep(0.01)

def setContrast(self, contrast):
if contrast < 0:
contrast = 0
if contrast > 0x0f:
contrast = 0x0f
self.writeCmd(0x39)
self.writeCmd(0x70 + contrast)

def setCursor(self, x, y):
if x < 0: x = 0
if y < 0: y = 0
addr = y * 0x40 + x
self.writeCmd(0x80 + addr)

def print(self, str):
for c in str:
self.writeData(ord(c))

if __name__ == '__main__':

import utime
import machine
from machine import Pin, I2C, SPI
import os
import sdcard

# Runnung lamp
led = Pin(25, Pin.OUT)

# LCD (I2C setting)
sensor_temp = machine.ADC(4)
conversion_factor = 3.3 / (65535)

i2c=I2C(0, scl=Pin(1), sda=Pin(0), freq=100000)
print('i2c devices found at')
devices = i2c.scan()

if devices:
for i in devices:
print(hex(i))
print()

utime.sleep(1)
lcd = ST7032(i2c)
lcd.setContrast(1)
lcd.clear()

# SD card (SPI setting)
spi = SPI(0, sck=Pin(2), mosi=Pin(3), miso=Pin(4))
sd = sdcard.SDCard(spi, Pin(5))
os.mount(sd, '/sd')
os.chdir('sd')

while True:
led.value(1) # Loop Start
# Temperature Measurement
reading = sensor_temp.read_u16() * conversion_factor
utime.sleep(1)
temperature = 27 - (reading - 0.706)/0.001721
tempStr = " {:5.1f}C".format(temperature)

# LCD output
lcd.setCursor(0, 0)
lcd.print('Temp:')
lcd.setCursor(0, 1)
lcd.print(tempStr)
print(temperature)

# SD Card output
with open("/sd/temp_measure.txt", "a") as file:
file.write(str(temperature) + "\r\n")

led.value(0)
utime.sleep(10)
 実行結果は以下の通りです。LEDが点灯しているのでSDカードに収集したデータを書き込んでいることが分かります。RaspiPico_LCD_SDC_210505.pngSDカードにはtemp_measure.txtというファイルが作成され、データが保存されました。こんな感じです。Result_file_210505.png時刻情報がないのは寂しいので、今後、RTCモジュールを追加しようと思います。Raspi PicoはArduinoのように遊べそうですね。

Raspberry Pi Picoで遊ぶ(1)

 コロナ禍でStay Homeな暮らしを送っています。ニュースを見ると都心部から郊外の観光地に人が出かけて集中しているという間抜けな報道がなされていますね。自分さえ良ければ良いという短絡的な考えですね。報道も報道で、外出している人々のみをクローズアップして、在宅で不要不急の外出をせずに我慢している所はフォーカスされていない(ニュースとしては面白みがないですが・・)。一方通行的な報道でマ○ゴミと言われる所以だと思います。おっと、悪態はこれぐらいにして・・。あくまでも個人の意見です・・。我慢のストレスが・・。

 明日から会社ですが、どこにも行けないので前回に引き続き、RaspiPicoと今回も遊びました。

 Raspi Picoは無線でセンサデータを飛ばせないので、少なくともデータをLCDで表示させたり、SDカードにデータ保存ぐらいはした方が良いかなと素朴に思いました。そこで、今回はLCDでRaspiPicoに実装されている温度センサのデータを表示させました。手元にあったI2Cで通信するLCDモジュールがあったのでそれを利用しました。回路図は以下の通りです。RaspiPico_LCD_curcuit_210505.pngコードはネット情報から仕入れ、ピン番号と一部のコードを変更して利用しました。

実行結果は以下の通りです。配線は相変わらず雑ですが、問題なく動作しました。
RaspiPico_LCD_210505.png次回はSDカードにデータを保存する所まで進めます。

Mac、Windows、Ubuntuのトリプル環境を作る

 昨年の大晦日に届いたM1チップ搭載のMac miniですが、購入当初は対応していないソフトも多く、使い慣れたIntelチップのMacBook Airに戻って作業をすることもありました。その後、Parallels Desktopというアプリを使ってWindows10のArm版をインストールしてVisual Studio2019が動かせる所までブログでお話ししていました。

 今回、Parallels Desktopの評価版から卒業して正規版を購入し、同時にUbuntuの環境も構築しました。簡単に構築できたので、その内容を紹介します。

まず、Parallels Desktopのアイコンを右クリックし、コントロールセンターを選択します。Parallels_setting0_210502.png右上の+マークをクリックすると、Parallels_setting1_210502.pngインストールアシスタントが立ち上がるので「続行」ボタンを押します。Parallels_setting2_210502.png新規作成画面に切り替わるので、Ubuntuを選択し「続行」ボタンを押します。Parallels_setting3_210502.pngUbuntu 20.04 ARM64を「ダウンロード」すると、Parallels_setting4_210502.pngUbuntuのインストール画面が立ち上がりますので、指示にしたがって進みました。Parallels_setting5_210502.pngユーザはParallelsになっていますが、起動後に修正可能です。Parallels_setting6_210502.png
 インストールも終え、改めてParallels Desktopを起動すると、WindowsとUbuntuの起動ランチャが立ち上がりました。Parallels_run0_210502.png起動すると、こんな感じでMac環境でありながら、WindowsとUbuntuが起動した状態が実現できました。Parallels_run1_210502.pngそれぞれのOSはウィンドウ最大化で利用すると、バックグラウンドでMacが動いていることも忘れてしまいそうです。というのも、M1チップのMacの処理速度が速いためかサクサクと動くからです。この1台で一人三役です。複数のOSを使う私には快適な環境が構築できました。フル活用していこうと思います。

Ambientを使う

 GWに入りましたが、昨年と同じようにコロナ禍で不要不急の外出を控え、自宅で静かに過ごそうとしています。3、4月と仕事が超多忙で体調が良くなかったので、ゆっくり休もうと思ってはいるのですが、何もしないのも気が滅入りますね。そこで、今回も電子工作で現実から逃避しています。やはり、手を動かしておいた方が良いと思ったので。

 今更ですが、今回は「IoTデータの可視化サービス」のAmbientを使ってみました。雑誌で何度も紹介されていたので、以前から名前は知っていましたが、利用したのは今回初めてです。

 手っ取り早く実行させたかったので(せっかちですね・・)、過去のブログから温湿度・気圧センサのBME280と、Wi-Fiが使えるマイコンのAE-ESP-WROOM-02(ESP8266)の配線図を探して、いざ接続。BME280_curcuit_210501.png
 まず、Ambientにデータを飛ばさずに、データ収集できることを確認しました。コードはGitHubから拝借。久々に触ったので、手こずりましたが、無事にデータを収集できました。BME280_res_210501.png
 次に、Ambientにデータを飛ばす所です。無料のユーザ登録し、自分のページの「チャンネルID」と「ライトキー」をメモっておきます。プログラムで記載する必要があるためです。Ambient_menu_210501.png元コードに以下の通り追加することでうまく行きました。
#include <ESP8266WiFi.h>
#include <Wire.h>
#include "Ambient.h"

#define BME280_ADDRESS 0x76
unsigned long int hum_raw,temp_raw,pres_raw;
signed long int t_fine;

WiFiClient client;
Ambient ambient;

const char* ssid = " Wi-Fi router name ";
const char* password = " Wi-Fi router password ";
unsigned int channelId = **** ; // Ambient's channel ID
const char* writeKey = " *********** "; // Write key

#define PERIOD 10 // 10sec

uint16_t dig_T1;
int16_t dig_T2;
//
// cut
//
int16_t dig_H5;
int8_t dig_H6;

void setup()
{
uint8_t osrs_t = 1; //Temperature oversampling x 1
uint8_t osrs_p = 1; //Pressure oversampling x 1
//
// cut
//
writeReg(0xF5,config_reg);
readTrim(); //

WiFi.begin(ssid, password); // Wi-Fi AP connection
while (WiFi.status() != WL_CONNECTED) { // Wi-Fi AP connection waiting
delay(100);
}
Serial.print("WiFi connected\r\nIP address: ");
Serial.println(WiFi.localIP());
ambient.begin(channelId, writeKey, &client); // Ambient initialize
}

void loop()
{
double temp_act = 0.0, press_act = 0.0,hum_act=0.0;
signed long int temp_cal;
//
// cut
//
Serial.print(hum_act);
Serial.println(" %");

ambient.set(1, temp_act); // Temperature: data1 set
ambient.set(2, hum_act); // Humidity: data2 set
ambient.set(3, press_act); // Atmospheric pressure: data3 set
ambient.send(); // data send

delay(PERIOD * 1000);
}
結果はAmbientにログインして確認できました。Ambient_res_210501.pngグラフの設定(グラフ種類、タイトル、軸名、線の色等)も簡単に変更できました。iPadからも当たり前ですが、確認できますね。Ambient_res_iPad_210501.png
 インターネット環境があれば簡単にマイコンデバイスからセンサデータを飛ばし、かつ確認できるのは大変便利ですね。無料なので、また遊んでみようと思います。

ご訪問者数

(Since 24 July, 2016)

タグクラウド


プロフィール

Dr.BobT

Author: Dr.BobT
興味のおもむくままに生涯考え続けるエンジニアでありたい。

月別アーカイブ

メールフォーム

名前:
メール:
件名:
本文: