RaspberryPiを用いたIoT製品制作

本書では、RaspberryPiの基本的な使い方を学びながら、Enviro+という環境センサーを接続し、 Python言語を使ってデータを取得・可視化するまでの流れを体験します。

前提知識

Linux系のOSの知識は不要ですが、ある程度、PC(またはMac)などの操作経験がある方を対象としています。 たとえば、本書中で「^G」または「Ctrl+G」と記載があれば、「Controlキーを押しながらGキーを押す」という意味になり、 このような操作が出来る、ということを期待しています(慣れてない人は今練習してください!)。

ファイルやフォルダの概念もある程度は知っているという想定です。

また、インストールを始めとしてほとんど全ての操作は「英語」で行っています。 この日本語の解説通りに進めば英語の知識はそれほど必要ありませんが、本書の内容を応用したり、予期しないエラーに遭遇してしまう場合など、 次のステップに進む際には、ある程度の「英語力」は必要になります。著者がそうしてきたように、 好きなことをやりながら辞書を片手(といっても今はPCにインストールできますけど)に一緒に英語も学んでいくスタイルが一石二鳥かと思います。

用語と基礎知識

本書に登場する主な言葉を解説します。コンピュータの仕組みを解説すると、どうしてもカタカナ(英語)の用語が頻繁に登場することになりがちです。なるべく平易な表現を心がけていますが、実際の現場で使われる語彙とかけ離れてしまうのも問題です。幸い、現在では、Wikipediaなどのインターネット上のリソースも豊富です。意味の分からない単語に出会ったらそのままにせず、Googleなどの検索エンジンで一度調べてみることをお勧めします。「◯◯とは」と検索すると用語集などのサイトにヒットしやすいように思います。著者の私は、この分野で10年以上働いていますが、未だに知らない(新しい)単語にしょっちゅう出会います。「知らない」のが普通です。「どうやって知るか」という方法を身につけることが大切です。コンピュータやインターネットの用語集を手元に持っておくのも良いかもしれません。

RaspberryPi

Raspi

イギリスで教育用に開発された手のひらサイズの小さなコンピュータ。5000円弱程度の廉価ながら、ディスプレイの入出力や通信機能など、コンピュータとして最低限の機能を備えています。拡張性も高く、様々な応用・工夫を施して利用している人も沢山います。公式サイトに利用事例や子供向けの工作課題などが多数掲載されています。2018年の6月には日本でも「Raspberry Pi 3 B+」モデルが発売され、これまで以上の性能・機能アップが図られています。小型化を図った「Raspberry Pi Zero」シリーズや「Raspberry Pi 3 A+」なども次々に発表され、用途に応じた選択肢の幅も広がっています。さらに翌年の2019年6月には「Raspberry Pi 4 Model B」が発売され、さらに高機能になっています。

RaspberryPiOS(Raspbian)

Linuxディストリビューションの一つである「Debian」をRaspberry Pi用にカスタマイズしたディストリビューションです。OSとしての基本機能のみならず、35000以上のパッケージが用意されており、カメラモジュールのドライバやコマンドも最初から整っており、ScratchやSonicなどの教育用のアプリが充実している点も魅力です。本書では、RaspberryPiOSをインストールしたRaspberry Piをベースに課題に取り組みます。

専門的な用途に使う場合は、RaspberryPiOS以外にもPidoraやArchLinuxなど様々な選択肢が用意されています。

Ruby

まつもとゆきひろ氏が開発したプログラミング言語です。ウェブサービスを立ち上げるための言語として有名ですが、その他にもたくさんの機能があって世界中で幅広く使われています。フリーソフトウェアとして配布されています(誰でもお金を払うことなく使えます)。執筆時点での最新バージョンは2.7ですが、RaspberryPiOSにはそれより少し古いバージョン2.5が最初からインストールされています。もちろん新しい方を使うほうがベターなんですが、基本的な用途では多少古いバージョンでも問題なく使えます。

本書の実習では登場しませんが、目的によってはとても有用なため、ここで名前のみ紹介しています。

Python

Python

オランダのGuido van Rossum氏が開発したプログラミング言語です。読みやすさを重視した言語構造が特徴で、Ruby同様、世界中で広く使われています。様々な専門分野のライブラリが充実していることも魅力です。最新のバージョン(3.x)では改善されているものの、日本語(マルチバイト文字)の扱いがRubyに比べると煩雑です。筆者は元々、Pythonを業務に用いてきた経緯がありますが、現在はRubyをメインにしています。Rubyの方が、日本語テキストの処理は容易だったという(日本人ならではの)理由です。

nano

RaspberryPiOSにデフォルトで搭載されたテキストエディタです。Linux(Unix)のテキストエディタといえば、viかemacsが有名ですが、nanoはこれらに比べてシンプルで覚えやすい操作性が特徴です(逆に、viやemacsは習得が困難な代わりに非常に高機能です)。本書ではnanoを使って、各種設定ファイルの編集や、プログラムの記述を行います。本書の後半で基本的な使い方を解説しています。

ターミナルの使い方

画面の左上にある黒っぽいアイコンをクリックするか、またはCtrl+Alt+Tを押して起動します。 黒いウィンドウの左上に以下のような表示が出てくれば正常に起動できています。

pi@raspberrypi:~ $

この表示をプロンプトと呼びます。RaspberryPiOSを始め、Linux系のOSでは、ここから様々な命令を コンピュータに対して送ることが出来ます。MacやWindowsにも似たような画面が存在します。MacのコマンドはLinuxと とてもよく似ていますが、Windowsだけはかなり違った言語を使います。

$のマークの後ろにカーソルがあることを確認したら、以下の命令を打ってみましょう。文字を打ち終わったらEnterキーを押します。

ls

lsは今いる場所のファイル及びディレクトリを一覧表示してくれるコマンドです。以下のように (ファイルやディレクトリの)名前がいくつか表示され、また元のプロンプトが表示されていることを確認してください。

pi@raspberrypi:~ $ ls
Documents  Pictures  Public..
(略)
pi@raspberrypi:~ $

このターミナルを主に使って、様々な設定やプログラムを書いていきます。

nanoエディタの使い方

本書では設定ファイルやプログラムの編集に「nano」エディタを使用しています。ここでは基本的な使用方法を紹介します。さらに詳しい使い方はnanoのヘルプページ(起動後に「^G」で表示)や公式サイトを参照してください。

起動は「nano」コマンドに続けて編集したいファイル名を指定します。新規作成の場合も同様です(保存時に指定した名前のファイルが生成されます)。

nano sample.txt

基本的な操作は、おそらくWindowsやMacを使ったことがあれば問題なく行えるのではないかと思います。カーソルキーで移動して、文字をタイプ、Backspaceキーで削除したりReturnキーで改行、などは一般的なテキストエディタと同じです。画面の下部に主要な操作が列挙してあります。白地に黒抜きで表示してある部分が、その操作を実行するためのキーです。「^G」は「Ctrl」キーを押しながら「G」キーを押す、という意味になります。

nano menu

以下の操作を覚えていれば本書で扱うくらいのプログラムの記述には十分です。

「^O」 - ファイルを保存します。
「^K」 - カーソルがある行をカットします。
「^U」 - カットした行をペーストします。
「^X」 - 終了します。未保存のファイルがある場合は、保存するか確認されます。

シンタックスハイライトや検索の機能もあって、そこそこ使える初心者向けエディタという感じですが、本格的にプログラミングを学びたいという場合には、viやemacsなどの高機能なエディタの使い方を学んでおくことをお勧めします。言語によっては、IDE(例えばNetbeansやEclipse)やAtomなどの後進のエディタ利用も選択肢に入れても良いかもしれません。Raspberry Pi OSには、ThonnyとGeanyという簡易的なIDEも標準でインストールされています。

Pythonの基本

GPIOやカメラなどRaspberry Piから外部のデバイスを操作するライブラリの多くはPythonという言語で提供されています。他の言語でも扱えないことはないのですが、インターネット上に情報が多く、安心して利用できるメリットがあります。そもそもPython自体が、とても覚えやすいシンプルな言語ですので、RaspberryPiを使いこなそう!と思ったら学んでおいて損はないと思います。ここでは、基本的な構文について幾つか紹介します。

まずは、Pythonを起動する方法と、画面に文字列「Hello, world!」を表示する方法です。

$ python
Python 2.7.10 (default, Feb  7 2017, 00:08:15)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> print(“Hello, world!")
Hello, world!

上記のサンプルはMacOSのターミナル上で実行しています。PythonはMacOSにも最初から入っているくらいメジャーな言語なんです。もちろん、RaspberryPiOS上でも、同じようにPythonを起動することができます。以降のサンプルはどちらでも同じように動作します。

「>>>(プロンプトと呼びます)」の記号の後ろに、Pythonに対する命令を入力して実行することができます。まず、以下のように数式を打って見ましょう。プロンプトの後ろを実際にタイプします。その次の行に表示されているのが実行結果です。

>>> 1 + 3
4
>>> 5 * 20 / 4
25

ちょっとした電卓がわりにも使うことが出来そうですね。「*(アスタリスク)」は掛け算、「/(スラッシュ)」は割り算の記号として使われます。

Pythonを終了するときは「Ctrl+D(Ctrlキーを押しながらD)」を入力します。

ちょっとした命令なら、上記のように起動、入力(実行)という手作業でも問題ありません、少し複雑なことをしたい場合は、プログラム(命令)を予めファイルに書いておいて、それを実行するという方法を取ることになります。Pythonのプログラムが書かれたファイル(スクリプト、などと呼びます)の名前を現在のディレクトリに置いて、以下のように指定します。

python foo.py

スクリプトの作成と保存は、前述のnanoエディタなどを使うと便利です。RaspberryPiOSで利用できるエディタは他にも色々ありますので、是非調べてみてください。

以降の実例に出てくるように、Pythonの特徴の一つに「インデント(行頭の空白またはタブ)による構文の表現」が挙げられます。例えば、以下の条件分岐の例は、「vが10以上なら、“v is bigger than 10.” と表示、vが10と等しければ、“v equals 10.”、それ以外ならば、“v is smaller than 10.” と表示」するプログラムです(これを実行すると何と表示されるか、分かりますか?)。「if .. elif .. else」のそれぞれに合致した時に実行されるブロック(行のかたまり)は行頭に4つの空白がある部分、として定義されます。言葉で説明すると、非常にややこしいですが、見慣れてくると(括弧を多用する他の言語と比べて)シンプルで分かりやすく感じられると思います。

v = 12

if v > 10:
    print("v is bigger than 10.")
elif v == 10:
    print("v equals 10.")
else:
    print("v is smaller than 10.")

条件分岐の他に、「繰り返し」もプログラム中でよく使う構文です。forとwhileの2つのキーワードがあります。forは「0から9まで(10回)繰り返す」というように回数が定まっている場合に便利です。

for i in range(0,10):
    print(i)

一方で、whileは「oooのあいだ」というように、ある条件が成立している(いない)間はずっと繰り返す、という場合に便利です。

i = 0
while i < 10:
    print(i)
    i += 1

まだまだ沢山ありますが、最後に関数の定義とモジュールのインポートについて紹介します。

ある処理のまとまりを関数として記述しておくと、後からそれを呼び出して何度も使うことができます。 以下の例ではhelloという名前の関数を定義し、その後、”John”、”Cat” という引数を渡して呼び出しています。引数とは関数の呼び出し時に渡す値のことで、「いんすう」ではなく「ひきすう」と読みます。

def hello(name):
    print("Hello, %s." % (name))

hello("John")
hello("Cat")

このプログラムを実行すると、以下のように表示されます。

Hello, John.
Hello, Cat.

モジュールのインポートは本書でも何度か登場します。Minecraftに接続する、GPIOのピンを介した通信をする、など外部とやりとりをしたり、プログラミング言語の基本機能として登場している以外のことをする場合に利用します(要するに現実のプログラミングではほとんど必ず使います!)。

例えば、「os」という名前のモジュールを使用する(=インポートする)ときは以下のように記述します。

import os

インポートされた後は、以下のようにして「os」モジュールが提供する関数やクラスを利用することが出来るようになります。以下のコードは、現在のディレクトリに存在するファイルの一覧を配列で返します。

os.listdir(".")

「os」モジュールには、オペレーティングシステムとやり取りをする際に必要な関数が多数用意されています。主にファイルの操作をする時に利用します。このように、プログラムの中で必要に応じて様々なモジュールをインポートしながら、必要な処理を実装して行きます。予めマシンにインストールいる以外にも沢山のモジュールが公開されており、これらをインターネット経由で導入・利用することも可能です。

配列とは、オブジェクト(数値や文字など)を連続して格納することが出来るデータ型です。 以下のように、定義したい値を[](大括弧、ブレース)で囲んで、コンマで区切ることで定義ができます。

a = [1,2,3]

他にも、範囲を指定して以下のように定義することができます。 rangeで1から4未満の整数の範囲を定義し、それをlistで配列に変換します。

a = list(range(1,4))

print関数で中身を表示することもできます。

>>> print(a)
[1,2,3]

配列の中の個々のデータのことを配列の「要素」と呼びます。 要素は後から追加したり削除したりすることが出来ます。

del(a[0])  # => [2,3]
a += [4]   # => [2,3,4]

他にも沢山の配列の操作用の関数や演算子が存在しています。 また、Pythonでは、配列に似た「タプル」というデータ型も存在します。 これは()(括弧、パレンシス)で囲んで定義します。多くの場面で配列と同じように振る舞いますが、 大きな違いとしてタプルは一度定義すると後から要素を変更できない、という点があります。

a = (1,2,3)

Enviro+の動作確認

プログラミングの詳細に入る前に、今回使用するEnviro+環境センサーが正常に動作することを確認します。

このセンサーはpHATと呼ばれる形状をしていて、RaspberryPiのGPIO端子に直接挿して使うことが出来ます。 配線の手間がなく簡単ですが、差し込む向きに注意してください。 RaspberryPi本体に対して、上から見てはみ出す部分が無いように差し込む、と覚えて頂けば大丈夫です。 写真は小型のRaspberryPi Zeroに差し込んだ状態です。

Enviro+

以下は、RaspberryPi OSがセットアップされネットワークにつながった状態を想定しています(ネットワーク接続が必須です)。 ターミナルを起動して、以下のようにコマンドを実行します。

sudo apt install git -y  # gitがインストールされていない場合
git clone https://github.com/pimoroni/enviroplus-python
cd enviroplus-python
sudo ./install.sh

途中で、サンプルコードをインストールするか聞かれます。yを押して(インストールして)ください。 再起動が必要な場合はそのように促されますので、再起動を実行してください。

sudo reboot

サンプルコードから適当なものを拾って実行して動作確認をすることが出来ます。 PMセンサーは外付けが必要なのでそれ以外で試しましょう。例えば、combined.pyを実行すると、組み込みのLCDに様々な計測値が表示されます。

cd Pimoroni/enviroplus/examples/
./combined.py

matplotlibの基本

matplotlibは、Pythonで様々なデータの可視化をすることができるライブラリ(モジュール)です。

以下のコマンドでインストールをすることが出来ます。

sudo pip3 install matplotlib

matplotlibのチュートリアルに様々な例があります。 全ての機能を覚えることは難しいので、この中から自分たちが使いたいグラフを探して、そのコードをカスタマイズするという使い方が一般的かと思います。

一番簡単な例を2つ実際に打って動作を確認してみましょう。

import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()  # Create a figure containing a single axes.
ax.plot([1, 2, 3, 4], [1, 4, 2, 3])  # Plot some data on the axes.

plt.show()
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2, 100)

fig, ax = plt.subplots()  # Create a figure containing a single axes.
ax.plot(x, x, label='linear')  # Plot some data on the axes.
ax.plot(x, x**2, label='quadratic')  # Plot more data on the axes...
ax.plot(x, x**3, label='cubic')  # ... and some more.
ax.set_xlabel('x label')  # Add an x-label to the axes.
ax.set_ylabel('y label')  # Add a y-label to the axes.
ax.set_title("Simple Plot")  # Add a title to the axes.
ax.legend()  # Add a legend.

plt.show()

Enviro+の基本

Pimoroni社のボードによっては専用のライブラリが用意されていることがあるのですが、 今回利用するEnviro+は「既に実績のある普及したセンサー」を組み合わせて作ってあるため、汎用的なライブラリを使用します。

まずは、気温(temperature)、気圧(pressure)、湿度(humidity)の取得例です。 BME280という名前のセンサーでこれらの3つの値を取得することができます。

#! /usr/bin/python3

from bme280 import BME280

b = BME280()

print(b.get_temperature())
print(b.get_pressure())
print(b.get_humidity())

次の例は明るさです。センサーの上に指をかざして影を作って値が変化することを確認できます。

#! /usr/bin/python3

from ltr559 import LTR559

l = LTR559()

print(l.get_lux())

ノイズとガスセンサーはenviroplusのライブラリを使用します。

#! /usr/bin/python3

from enviroplus.noise import Noise

n = Noise()

print(n.get_noise_profile())
#! /usr/bin/python3

from enviroplus import gas

data = gas.read_all()

print(data.oxidising)

この他に粒子(PM)センサーも拡張して接続することが可能です。

また、Enviro+には小さなLCD(画面)も付属しています。使い方にも依りますが、 例えば、工場や倉庫など、ディスプレイを置くスペースが無いときにRaspberry Pi Zeroの本体とこのセンサーだけを 置いて、必要な数値やグラフががいつでも読めるようにしておくと便利そうです。少し複雑ですが、以下にサンプルコードを掲載します。

#!/usr/bin/env python3

import ST7735
from PIL import Image, ImageDraw, ImageFont
from fonts.ttf import RobotoMedium as UserFont

# Create LCD class instance.
disp = ST7735.ST7735(
    port=0,
    cs=1,
    dc=9,
    backlight=12,
    rotation=270,
    spi_speed_hz=10000000
)

# Width and height to calculate text position.
WIDTH = 160  # = disp.width
HEIGHT = 80  # = disp.height

# Initialize display.
disp.begin()

# New canvas to draw on.
img = Image.new('RGB', (WIDTH, HEIGHT), color=(0, 0, 0))
draw = ImageDraw.Draw(img)

# Text settings.
font_size = 25
font = ImageFont.truetype(UserFont, font_size)
text_colour = (255, 255, 255)
back_colour = (0, 170, 170)

message = "Hello, World!"
size_x, size_y = draw.textsize(message, font)

# Calculate text position
x = (WIDTH - size_x) / 2
y = (HEIGHT / 2) - (size_y / 2)

# Draw background rectangle and write text.
draw.rectangle((0, 0, WIDTH, HEIGHT), back_colour)
draw.text((x, y), message, font=font, fill=text_colour)
disp.display(img)

# Keep running.
try:
    while True:
        pass

# Turn off backlight on control-c
except KeyboardInterrupt:
    disp.set_backlight(0)

matplotlibの作例

Pimoroni Enviro+と組合わせて使えそうな作例です。もっと多くの例が公式サイトのGalleryにあります。

正弦波・余弦波のアニメーション(一箇所に描画)

animate1
#! /usr/bin/python3

from matplotlib import pyplot
from matplotlib import animation
import math

fig, ax = pyplot.subplots()
x = list(range(0,120))
y1 = [0] * 120
y2 = [1] * 120
line1, = ax.plot(x, y1, label="sin")
line2, = ax.plot(x, y2, label="cos")
ax.legend()
ax.set_ylim([-1.1,1.1])

def animate(f):
    global y1,y2
    del(y1[0])
    del(y2[0])
    y1 += [math.sin(f / 10)]
    y2 += [math.cos(f / 10)]
    line1.set_ydata(y1)
    line2.set_ydata(y2)

ani = animation.FuncAnimation(fig, animate, interval=300)

pyplot.show()

正弦波・余弦波のアニメーション(別々に描画)

animate2
#! /usr/bin/python3

from matplotlib import pyplot
from matplotlib import animation
import math

fig, ax = pyplot.subplots(2,1)
x = list(range(0,120))
y1 = [0] * 120
y2 = [1] * 120
line1, = ax[0].plot(x, y1, label="sin")
line2, = ax[1].plot(x, y2, label="cos")
ax[0].legend()
ax[0].set_ylim([-1.1,1.1])
ax[1].legend()
ax[1].set_ylim([-1.1,1.1])

def animate(f):
    global y1,y2
    del(y1[0])
    del(y2[0])
    y1 += [math.sin(f / 10)]
    y2 += [math.cos(f / 10)]
    line1.set_ydata(y1)
    line2.set_ydata(y2)

ani = animation.FuncAnimation(fig, animate, interval=300)

pyplot.show()

温湿度のアニメーション

animate3
#! /usr/bin/python3

from matplotlib import pyplot
from matplotlib import animation
from bme280 import BME280

fig, ax = pyplot.subplots()
x = list(range(0,120))
y1 = [0] * 120
y2 = [1] * 120
line1, = ax.plot(x, y1, label="humid")
line2, = ax.plot(x, y2, label="temp")
ax.legend()
ax.set_ylim([0,100])

b = BME280()
# from smbus import SMBus
# b = BME280(i2c_dev=SMBus(1))
# b.setup(mode="normal")

def animate(f):
    global y1,y2
    del(y1[0])
    del(y2[0])
    y1 += [b.get_humidity()]
    y2 += [b.get_temperature()]
    line1.set_ydata(y1)
    line2.set_ydata(y2)

ani = animation.FuncAnimation(fig, animate, interval=1000)

pyplot.show()

明度・ノイズのアニメーション

animate4
#! /usr/bin/python3

from matplotlib import pyplot
from matplotlib import animation
from ltr559 import LTR559
from enviroplus.noise import Noise

fig, ax = pyplot.subplots(2,1)
x = list(range(0,120))
y1 = [0] * 120
y2 = [0] * 120
line1, = ax[0].plot(x, y1, label="lux")
line2, = ax[1].plot(x, y2, label="noise")
ax[0].legend()
ax[0].set_ylim([0,1000])
ax[1].legend()
ax[1].set_ylim([0,1])

l = LTR559()
n = Noise()

def animate(f):
    global y1,y2
    del(y1[0])
    del(y2[0])
    y1 += [l.get_lux()]
    y2 += [n.get_noise_profile()[3]]
    line1.set_ydata(y1)
    line2.set_ydata(y2)

ani = animation.FuncAnimation(fig, animate, interval=300)

pyplot.show()

ノイズの成分別アニメーション

animate5
#! /usr/bin/python3

from matplotlib import pyplot
from matplotlib import animation
from enviroplus.noise import Noise

fig, ax = pyplot.subplots()
x = list(range(0,120))
y1 = [0] * 120
y2 = [0] * 120
y3 = [0] * 120
y4 = [0] * 120
line1, = ax.plot(x, y1, label="low")
line2, = ax.plot(x, y2, label="mid")
line3, = ax.plot(x, y3, label="high")
line4, = ax.plot(x, y4, label="total")
ax.legend()
ax.set_ylim([0,1.0])

n = Noise()

def animate(f):
    global y1,y2,y3,y4
    del(y1[0])
    del(y2[0])
    del(y3[0])
    del(y4[0])
    low,mid,high,amp = n.get_noise_profile()
    y1 += [low]
    y2 += [mid]
    y3 += [high]
    y4 += [amp]

    line1.set_ydata(y1)
    line2.set_ydata(y2)
    line3.set_ydata(y3)
    line4.set_ydata(y4)

ani = animation.FuncAnimation(fig, animate, interval=300)

pyplot.show()

制作課題

  1. matplotlibを使って、室内の環境をリアルタイムにグラフ表示するプログラムを作成しましょう。
  2. グラフの組み合わせを変更して、より(あなたにとって)見やすい表示にカスタマイズしてみましょう。
  3. (高難度)Enviro+のボードにもグラフを表示しましょう。本書の前半のサンプルコードの他に、Pimoroni社の提供するサンプルプログラムも見つけだして参考にしてください。

補遺

参考資料

本書について

本書は、宮崎県ソフトウェアセンター様から「企業との協働型人材育成事業 ICT 分野『ラズベリーパイを用いた IoT 製品制作』」事業の委託をうけ、 5日間(計5時間)の高校生向け授業のために執筆した資料です。2020年9月時点の情報をベースに記載しています。

「CC BY 4.0」に則った再利用・再頒布が可能です。

著者紹介:伊藤陽生 株式会社ランバーミルの代表取締役。中小規模のシステム開発を経験は20年近く続けています。 Cから始まりJava、JavaScript、Objective-C、PHP、 Python、SQLなどを主に業務で扱っています。 Linuxサーバの構築だったり、ウェブサイトのデザインをしたり、これらの分野の研修講師として働いていたこともあります。 技術の移り変わりの 速いこの領域においては、それぞれの経験値そのものよりも、 いかに新しい技術にうまくキャッチアップし、チームで共有し、身近な問題の解決に結びつけられるか、 という、もう一段抽象的な技能こそが重要なのではないかと考えています。

本書に関するお問い合わせ、誤字・脱字等のご指摘は、下記URLにお願い致します。

https://lumber-mill.co.jp/contact.php