Python Raspbeyyi Pi プログラム 回路 電子工作 高専3年生

【Raspberry Pi】電子工作入門②~ファイル保存とグラフの作成~【Python】

前回はこちら

Raspberry Pi はマイコンと同じようにセンサーの電圧値などを簡単に計測できます。

またPCと同じようにファイル操作、グラフの作成などの作業も行うことが出来ます。

今回はそんなRaspberry Piの特性を活かして、電圧情報をファイルとして保存し、またPython言語を使ってグラフ表示を行う方法を学びます。

Pythonでファイルにデータを保存する方法

プログラムでデータを扱うとき、ただ画面に表示するだけでなく「ファイルに保存する」ことがよくあります。
測定データを残したり、実験結果をまとめたり、後からグラフ化したりするのに欠かせません。
ここでは Python でのファイル保存の基本を順を追って学んでいきましょう。

1. そもそもファイルとは

  • コンピュータ上の「ノート」のようなものです。
  • 中に文字や数字を書き込んだり(保存)、後で読み返したり(読み込み)できます。
  • .txt、.exeなどファイル名の最後の文字(拡張子)によってファイルの種類を見分けます。
  • 種類によって中身のデータの書き方が変わります。
  • Pythonでも「ファイルを開いて → 書く → 閉じる」という流れで操作します。

2.最も基本的な書き込み

# "sample.txt" という名前のファイルを作成して書き込み
f = open("sample.txt", "w")  # "w" は write(書き込み)の意味
f.write("こんにちは、ファイル!\n")  # 1行書く
f.write("Pythonでファイルに保存できます。\n")
f.close()  # 忘れずに閉じる
  • これを実行すると、同じフォルダに sample.txt ができます。
  • 中身をテキストエディタで開くと、書いた文章が入っています。

キーワード:

  • "w" … 上書きモード。新しく作る/中身を消して書き直す。
  • "a" … 追記モード。既存ファイルの最後に追加。
  • "r" … 読み込みモード。

3.with文を使った保存

Pythonでは with open(...) as f: と書くのが一般的です。
自動で閉じてくれるので、閉じ忘れの心配がありません。

with open("sample2.txt", "w") as f:
    f.write("1行目\n")
    f.write("2行目\n")

4.繰り返し保存する場合

例えば、0〜9までの数字をファイルに1行ずつ書く場合:

with open("numbers.txt", "w") as f:
    for i in range(10):
        f.write(f"{i}\n")  # f-stringで整形して書き込み

出力される numbers.txt はこんな感じです。

0
1
2
3
...
9

5.CSVで保存する

**Comma Separated Values(カンマ区切り値)**の略です。

表のデータを保存することが出来、後からExcel、Googleスプレッドシートなどで開くことが出来ます。

time,value
0.0,10
0.1,12
0.2,15

6.CSVの書き込み

Pythonでは csv モジュールを使うと便利です。
writer.writerow([...]) で1行ずつリストの形でデータを書けます。

import csv

with open("data.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["time", "value"])   # ヘッダー(列名)
    writer.writerow([0.0, 10])
    writer.writerow([0.1, 12])
    writer.writerow([0.2, 15])

実行後に data.csv を開くと、表のようなデータが入っています。
newline="" を付けるのは、余計な空行が入るのを防ぐためです。

7.繰り返してデータを記録する

測定値を時系列で保存したい場合は、ループで繰り返し書き込めばOKです。

import csv, time

with open("log.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["time_sec", "value"])

    t0 = time.perf_counter()   # 実行開始時刻を基準に
    for i in range(5):         # 5回だけサンプルを保存
        t = time.perf_counter() - t0
        value = i * 10
        writer.writerow([t, value])
        time.sleep(0.5)

これを実行すると、0.5秒ごとに測った「時刻」と「値」が5行分記録されます。
後からグラフにすれば「時間に応じて値が変わる様子」が見えるわけです。

問題4

可変抵抗を ADC0834(CH0)で読み取り、0.1秒ごとにCSVファイルへ保存しなさい。

  • ファイル名は実行時刻入り(例:20250922_153045_adc0834.csv
  • 列は time_sec, raw, voltage_V
  • 記録中は画面にも time, raw, voltage を表示
  • Ctrl+Cで止めたら保存先パスを表示

保存したCSVを Excel などで開き、可変抵抗を回したときに値がどう変わるか確認してみましょう。

解答例はこちら
import spidev
import time
import csv
from pathlib import Path
from datetime import datetime

# === SPI 初期化 ===
spi = spidev.SpiDev()
spi.open(0, 0)                 # bus=0, CE0
spi.mode = 0                   # CPOL=0, CPHA=0
spi.max_speed_hz = 250_000     # 200〜400kHzが安定
spi.bits_per_word = 8

# === ADC0834 設定 ===
VREF = 3.3                     # 基準電圧(3.3V)
CMD_BASE = 0x08                # SGL=1(単端子)ビット

def _cmd_for_channel(ch: int) -> int:
    """ADC0834へ送る2バイト目のコマンドを生成(chは0..3)"""
    return (CMD_BASE | (ch & 0x03)) << 4

def read_adc0834_once(ch: int = 0) -> int:
    """指定チャネルを1回だけ読み取る(0..255)"""
    r = spi.xfer2([0x01, _cmd_for_channel(ch), 0x00])
    # ダミー0の後の MSB→LSB 8bit を取り出す(v4式)
    return ((r[1] & 0x0F) << 4) | (r[2] >> 4)

# === ログ保存の準備(ホーム直下に adc_logs) ===
home = Path.home()                     # 例: /home/ando
log_dir = home / "adc_logs"
log_dir.mkdir(exist_ok=True)           # 無ければ作成
filename = datetime.now().strftime("%Y%m%d_%H%M%S_adc0834.csv")
filepath = log_dir / filename

interval = 0.1   # 0.1秒ごとに記録

try:
    with open(filepath, "w", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(["time_sec", "raw", "voltage_V"])  # ヘッダー

        t0 = time.perf_counter()
        print(f"Logging to: {filepath}  (Ctrl+C で停止)")

        while True:
            raw = read_adc0834_once(0)          # CH0
            voltage = raw * VREF / 255
            t = time.perf_counter() - t0

            # 画面にも表示(整形)
            print(f"{t:8.3f}, raw={raw:3d}, {voltage:6.3f} V")

            # CSVに書き込み(文字列/数値どちらでもOK)
            writer.writerow([f"{t:.3f}", raw, f"{voltage:.4f}"])
            f.flush()                            # 安全のため都度フラッシュ

            time.sleep(interval)

except KeyboardInterrupt:
    print("\nStopped by user.")
    print(f"Saved file: {filepath}")

finally:
    spi.close()

Pythonでグラフ表示

実験や測定では数値を集めるだけでは変化が分かりにくいです。
グラフにすると「どう増えているか」「どこで変化したか」が一目で理解できます。
Pythonには matplotlib という強力なグラフ描画ライブラリがあります。

1.ライブラリのインストール

Raspberry Pi OS では apt を使ってインストールするのが簡単で安全です。

1.ターミナル(黒い画面のアプリ)を開く

2.次のコマンドを入力して Enter キーを押す

sudo apt update
sudo apt install -y python3-matplotlib python3-pandas

2.インストール確認

Thonnyを起動し、シェル(>>> のところ)で以下を入力してください。

import matplotlib, pandas
print(matplotlib.__version__, pandas.__version__)

3.matplotlibを使ったグラフの基本

import matplotlib.pyplot as plt

x = [0, 1, 2, 3, 4]
y = [0, 1, 4, 9, 16]

plt.plot(x, y)                  # 折れ線グラフを描画
plt.xlabel("X axis")            # x軸ラベル
plt.ylabel("Y axis")            # y軸ラベル
plt.title("Sample Graph")       # タイトル
plt.grid(True)                  # 補助線を表示
plt.show()                      # グラフを表示

実行すると、新しいウィンドウにグラフが表示されます。
※ ラベルはすべて英語で書くと、フォントエラーが出ません。

4.CSVファイルを読み込んでグラフ化

前の課題(問題4)で保存した CSV ファイル(例:adc_logs/20250922_153045_adc0834.csv)を読み込み、電圧の変化をプロットしてみましょう。

import pandas as pd
import matplotlib.pyplot as plt

# CSVファイルを読み込む(自分のファイル名に置き換える)
df = pd.read_csv("adc_logs/20250922_153045_adc0834.csv")

# 折れ線グラフを描画
plt.plot(df["time_sec"], df["voltage_V"])

# グラフの整形
plt.xlabel("Time [sec]")
plt.ylabel("Voltage [V]")
plt.title("ADC0834 Logging Data")
plt.grid(True)

plt.show()

問題5

CSVファイルをグラフ化する基本コードをベースに、

区間の拡大

線のスタイル変更

移動平均の追加

特徴点の強調表示

など、自分なりに一工夫したグラフを作成しなさい。

回答例はこちら

回答例(区間を絞って表示)

# 0〜5秒のデータだけ抽出して描画
df_zoom = df[df["time_sec"] <= 5]
plt.plot(df_zoom["time_sec"], df_zoom["voltage_V"])
plt.xlabel("Time [sec]")
plt.ylabel("Voltage [V]")
plt.title("Zoomed View (0–5 sec)")
plt.grid(True)
plt.show()

回答例(移動平均をかけて滑らかに)

# 5点移動平均を計算
df["smoothed"] = df["voltage_V"].rolling(window=5).mean()

plt.plot(df["time_sec"], df["voltage_V"], alpha=0.4, label="Raw")
plt.plot(df["time_sec"], df["smoothed"], color="red", label="Smoothed (5-point)")
plt.xlabel("Time [sec]")
plt.ylabel("Voltage [V]")
plt.title("Raw vs Smoothed")
plt.grid(True)
plt.legend()
plt.show()

回答例(発展③:最大値を強調表示)

max_idx = df["voltage_V"].idxmax()
max_time = df["time_sec"][max_idx]
max_val = df["voltage_V"][max_idx]

plt.plot(df["time_sec"], df["voltage_V"])
plt.scatter(max_time, max_val, color="red", label=f"Max: {max_val:.2f} V")
plt.xlabel("Time [sec]")
plt.ylabel("Voltage [V]")
plt.title("Max Value Highlighted")
plt.grid(True)
plt.legend()
plt.show()

次回はこちら

Follow me!

-Python, Raspbeyyi Pi, プログラム, 回路, 電子工作, 高専3年生
-, , , , ,

PAGE TOP