t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

良書:マンガでわかる物理のキホン

最近読んだ物理の入門本がとても良かったのでご紹介。
扱っている範囲は高校で習うような初頭力学(古典力学)で、授業中に寝てた層(私)にぴったり。

購入の背景

最近LEGOのライントレースボットの改良に取り組んでいる中で、PID制御について調べ始めている。
それで制御工学系の書籍を書店でパラパラとめくってみるが、書いてある数式の意味が分からず購入を断念。

何がややこしいかって、vとかaとか、何の説明もなくいきなりぶっこんでくるのだ。
多分、物理(力学)を真面目にやってた人からすれば、自明のことなんだろう。

「おいおい、このシチュエーションでvっつったらvelocity(速度)一択だろう。aはacceleration(加速度)に決まってるじゃないか。」

いや、それが分からんのだ。。
(ひょっとしたらその本にもちゃんと書いてるのかもしれない。パラパラめくっただけだし。)

それでまず物理センスを磨く必要があるなと思ってこの本を購入した。

分からないのではなくて多分知らないだけだ。知らないことは覚えれば良い。

書籍の構成

扱っている範囲

  • 力のつり合いと作用・反作用の法則
  • 慣性の法則と運動方程式
  • 重さと質量
  • 重力と万有引力
  • 運動の相対性
  • 運動量保存の法則
  • エネルギー保存の法則

マンガ部分

マンガを謳う書籍の中には、キャラは適当で延々と解説がかかれている本(漫画詐欺だ!)もあるけど、この本はちゃんとマンガになっていてキャラ同士の掛け合いもそこそこ楽しめる。
240ページ中、がっつりコマ割り漫画になっているのが101ページ(数え間違ってたらごめんなさい)。
こうした書籍では割と多いほうだと思う。

一部引用させていただくと、雰囲気はこんな感じ。

地球周回軌道に乘っている宇宙船の中は無重力じゃなくて、正確には無重量ということらしい。こういうウンチク知識をぶっこんでくるので飽きない。

人間は表情から色々と相手の感情を読み取る能力が発達しているので、表情の描き分けがちゃんとできていると飽きずに最後まで楽しく読めるのかもしれない。この本はアタリ。

解説部分

240ページ中、トーク形式の解説が117ページ(数えま[略])。

これもキャラ同士の掛け合いになっていて、割と読みやすい。

実は私はこのバネ2倍伸びると思ってた。。

ただ事象を解説するのではなく、逐一興味深い実例を紹介しながら説明されているので飽きない。

もちろん必要最小限の数式は出てくるが、式そのものは加減乗除くらいのもので、\sumとか、f(x)とか\intとかそういうアブナイ人たちは出てこないので数学が苦手な方もご安心を。

物理の場合は計算するための数式っていうよりは、要素と要素の関係を表すのにたまたま数式が便利でしたってだけで、関係式といったほうが近い気がした。
まぁ初頭物理の範囲なのでその先は知らないけど。。

総評

「買いましょう」

以上

Satisfactory Update 6 グラフィックの進化について

6月14日にSatisfactory Update 6がExperimental Branchとして公開されたので、遊び初めている。

Experimental Branchというのはアップデート体験版のような位置付けで、バグをつぶし切れていない状態で公開されるので色々と不具合があるけど新しい要素を先行して遊べるというバージョンである。

体験版といってもセーブデータは製品版からそのまま持ってこれるし、製品版が正式にアップデートされる際にも製品版にそのままデータを移すことができるので、バグに目をつぶって一足先に更新しているという感じ。

注意点としてはExperimentalでデータを上書き保存してしまうと製品版のアップデートが追い付くまで戻せなくなるので必ずバックアップが必要なことと、製品版がアップデートされたらなるべく早く製品版のブランチに戻さないと、そのうちExperimentalとのセーブデータの互換性がなくなってしまう。開発元のCoffeestain StudioやSatisfactoryのTwitterアカウントをフォローする等して情報収集はしておくと安心。

SteamのPropertyのBETASというメニューからexperimentalを選択することでUpdate 6 Experimentalが遊べる

さて、Update 6は冒険要素のアップデートがメインである。今回のアップデートの目玉として、2つのバイオームの大幅な地形変化と敵の挙動変更、武器のバラエティ追加が挙げられる。

なかでも私が一番気に入ったのはグラフィックの進化である。
空模様や自然光などのライティングが明らかに変わっている。

実を言うと、第一印象は微妙だった。
なんかUpdate 4の頃に戻ったような能天気な晴天。

少し淡くて、深みのあるUpdate 5の空の方を気に入っていたのに。。と。

しかしすぐにその評価は覆ることになる。
この雲、刻々と形を変えて動く。

Update 5の空も確かに綺麗だったけど、雲は固定で快晴にもならなければ曇天もない。
Update 6の空は様々な表情を見せてくれるためつい写真におさめたくなる。


今にも降り出しそうな空を見ていたらこのあと雨が降り出した。

今回から実装された雨。ドローンポートがしっとり濡れて光る。


あとはライティングにも結構大きな変化が見られる。
以下は同じセーブデータを使って全く同じゲーム時間帯に撮り比べたスクリーンショットである。

Update 5の頃は室内や夜がとにかく暗くて少しどんよりした気持ちになっていたけど、Update 6になってからとにかく明るい。

上の写真は特に採光用の窓がしっかり機能していて昼間は室内でも気持ち良い。

下の写真は月明りが窓から入り込んでいるということだと思うけど、かなり明るくなって嬉しい。

明るすぎて現実感が無い?いやここは異星なので、、、

そもそも月明りこんなんだし。

まあ、この惑星地球じゃないのでそもそも月じゃないし、ひょっとしたら光が弱い別の恒星か何かかもしれないけど。

あと人間の目が暗闇に慣れる仕組みもUpdate 6から再現されているらしい。

正直、もうUpdate 5でグラフィックは十分に綺麗だと思っていた。
でもそれは間違いだった。

FPV(一人称視点)のゲームではグラフィックの美しさがそのまま臨場感になる。
ただのゲームなんだけど、ほんとうに自然の中を駆け回っているような、とてもリラックスした気持ちにさせてくれる。

ということで、今回はグラフィックの進化について紹介した。
Satisfactory、気になる方はSteamでチェック。

通常3,180円のところ、今ちょうどSteamのサマーセール中で2022年7月8日まで1,908円でお得に購入できるようだ。

どんなゲームなの?って方のために、最後にトレーラーをいくつか貼っておこうと思う。

↓初期のものだけど楽しそうな要素が一通り詰まってて好き (2分15秒)
youtu.be

↓Update 3 パイプが導入された。私はここからスタート。(2分32秒)
youtu.be

↓Update 4 ホバーパックとかドローンとか。(2分20秒)
youtu.be

↓Update 5 建築のバラエティが大幅に強化。(1分46秒)
youtu.be

↓Update 6 トレイラーまだ出てないので代わりにハムさんの動画。まとめ早い!(10分34秒)
youtu.be

以上

Python Processingで写真を六角形に切り取って並べる

今回は写真を6角形に切り取って並べるPython Processingコードのメモ。

出来上がりのイメージはこんな感じ。

上記はSatisfactoryというゲームで撮りためた風景スクリーンショットである。
このゲームは最近Update 6という大型パッチがリリースされたところで、地形アップデートにより更に景観が美しくなっているので写真撮影が止まらない。

一枚つずつ紹介するのも多すぎるので小さくして並べようと思ったんだけど、パワポで縮小とトリミングを手でやり始めたところズレるしサイズも狂うしなんか微妙な見た目になってしまった。

そこでProcessingを使って綺麗にしようと思ったんだけど、どうせやるなら何か面白い形にしたいというのが発端。

コード

注意) 私の環境では画像データ38枚の読み込みに30秒ほどかかった。
大きな画像が大量に入ったフォルダーを指定するとフリーズする可能性があるのでご注意。

import glob
def setup():
    background(0)
    global radius
    radius = 87 #(1)
    size(1280, 720)
    
    #(2) (参考) https://discourse.processing.org/t/unexpected-strokes-are-shown-on-p2d-pgraphics/21660
    hexagon = createShape()
    hexagon.setStroke(False)
    hexagon.beginShape()
    for i in range(0,6): #(3)
        angle = i * PI/3 + PI/6 #(4)
        hexagon.vertex(cos(angle) * radius, sin(angle)*radius) #(5)
    
    hexagon.endShape(CLOSE)
  
    global photo
    photo = []
    
    files = glob.glob(ur"C:\Users\ho_\OneDrive\docs\My Games\FactoryGame\Screenshots\*")
    for file in files:
        print(file)
        p = loadImage(file)
        
        #(6)
        if (p.height < p.width):
            p.resize(radius*4, radius * 4 * p.height/p.width)
            cutlocation_x = p.width-radius*2
            cutlocation_y = p.height-radius
        else:
            p.resize(radius * 4 * p.width / p.height, radius*4)
            cutlocation_x = p.width-radius
            cutlocation_y = p.height-radius*2
 
        #(7) (Ref)https://forum.processing.org/two/discussion/18819/how-to-copy-a-triangle-out-of-an-image.html
        maskImage = createGraphics(p.width,p.height)
        maskImage.beginDraw()
        maskImage.shape(hexagon,cutlocation_x, cutlocation_y)
        maskImage.endDraw()
        p.mask(maskImage)

        #(8)
        p = p.get(cutlocation_x-radius, cutlocation_y-radius, radius*2, radius*2)
        photo.append(p)
    
def draw(): #(9)
    x = 0
    y = 0
    for i in photo:
        if y % 2 == 0:
            image(i, radius*1.8*x,radius*2 * 0.79 * y)
            x_limit = 8
        else:
            image(i, radius*0.9+radius*1.8*x,radius*2 * 0.79 * y)
            x_limit = 7
            
        x = x + 1
        if x == x_limit:
            x = 0
            y = y + 1

解説

  • (1) 正N角形の頂点は円に接するので、正N角形のサイズを頂点が接する円の半径radiusとして設定する。
  • (2) 頂点が円に接するということは、円周をN分割した切れ目のところがN角形の頂点になる。
  • (3) 今回は6角形なので頂点数、つまりループ回数は6回。
  • (4) 弧度法では360度が 2\pi ラジアンなので、6分割すると\frac {\pi}{3}である。これにiを掛けることで角度が求まる。また、図形を30度傾けるために\frac {\pi}{6}を足し合わせている。
  • (5) vertexはx,yを指定して図形に頂点を追加する命令である。三角関数でラジアン角度を座標に変換する。6回繰り返すと六角形の完成。
  • (6) 横長の画像か縦長の画像かを判定して縮小サイズと切り取り位置を分岐させている。

  • (7) 6角形をマスク画像としてPGraphics型のマスクイメージを作成し、画像に適用。
  • (8) マスクは画像全体に掛かるため、必要部分のみ切り取り、加工が完了したイメージをphotoリストに追加。
  • (9) xを増加しながら横に並べていき、規定数に達したらyを増やしてxをリセットする。yが偶数のときはxの最大個数を一つ減らす。

終わりに

今回はProcessingで私がやりたいことを実現できたので、今後Processingを活用するシーンが増えるという手ごたえを感じた。
また何か面白いアイデアが湧いたらProcessingを使ってみようと思う。

以上。

6/19 追記 1

指定するフォルダーによって読み込みできないバグに遭遇したので修正した。
どうやらUnicodeの文字化けと、バックスラッシュが特殊文字と解釈されていたのが原因のようで、パスの文字列にurを付けてUnicdeのraw文字列にすることで解決した。

# Before
files = glob.glob("C:\Users\ho_\OneDrive\docs\My Games\FactoryGame\Screenshots\*")

# After
files = glob.glob(ur"C:\Users\ho_\OneDrive\docs\My Games\FactoryGame\Screenshots\*")

(参考)
www.javadrive.jp

6/19 追記 2

スマホで撮影した縦長のJPEGを読み込むと横倒しになってしまう問題があることが分かった。
どうやらそれらのJPEGはもともと横倒しの画像にEXIFという方向の情報を付けることで縦向きとしてふるまっているらしく、WindowsのプレビューなどのEXIFに対応しているソフトでは縦に表示されるが、EXIFに対応していないソフトでは本来のデータどおり横向きに表示されてしまうらしい。

残念ながらProcessingはEXIFに対応してなさそうで、外部ライブラリ等を色々調べた結果断念した。
回避策として一番簡単なのはPNG形式に変換してしまうことだろうか。

私の場合はWebで一括置換するツールがあったので利用してみた。ただ本来横倒しでEXIFによって縦になっているJPEGをPNG化したら横倒しに戻ってしまったので、Windows標準のビューワーで回転させて戻した。
※このビューワーも曲者で、回転させてしばらくしないと保存されないのでサクサク回転させていくと中飛ばしになってしまうということが発生する。

6/19 追記 3

縦長の画像の場合に切り取る位置と縮尺が想定と違うことが分かった。
あんまり理論的に考えずにトライ&エラーで完成したと思い込んだ為のバグ。
これは時間を見て直そうと思う。

SymPyで三角関数のテイラー展開 ~ sin()の中身はどうなってるのか

前回desmosという数学ソフトのサンプルとしてテイラー展開を示したが、私にはよく分からないと書いた。

ただこれ、よく思い出してみると三角関数のsin()の実装がどうなっているのか気になって最近調べたサイトで見かけたことがある。
hackemdown.blogspot.com

つまり以下の式を使えばsin関数を自分で作ることができるということらしい。
 \displaystyle \sum_{n=0}^{a} \frac{ (-1)^{n}x^{2n+1}} {(2n+1)!}

※少し話が脱線するが、はてなブログはTeX記法が使えるので上記の数式は以下のように書ける。

[tex: \displaystyle \sum_{n=0}^{a} \frac{ (-1)^{n}x^{2n+1}} {(2n+1)!}]

少し複雑だけど有志がチートシートを公開しているのではてなブログで数式を書きたい時はTeX(てふ)について調べてみると良いかと思う。

…話をもどして、そのときはVBAでやろうとして階乗ですぐオーバーフローになったり、どこかで式の理解を間違えたのか値がズレすぎて諦めていた。

今回はSympyを使ってリベンジしてみる。

コードは以下の通り。

# 準備
import math
from sympy import init_printing, symbols, Sum, factorial, summation, solve
from sympy.plotting import plot
%matplotlib inline
init_printing(use_latex='mathjax')

x,a,n = symbols('x a n')

# 式の組立
y = Sum((-1)**n*x**(2*n+1)/factorial(2*n+1), (n, 0, a))

# 繰り返し回数 a の決定とプロット
f = y.subs({a:20})
plot(f)

Google Colaboratory でやってみると次の通り。

おお、サイン波っぽいのでた!
できてるようだ。

しかし実際にxを代入して計算させたいけどうまくいかなかった。
次のように文字式のまま表示されてしまい、solveしても結果が現れない。

なんかやりようはあるんだろうけど一旦断念してSum()の代わりにsummation()という関数を使うことにする。

関数f2として作成するコードがこちら。

f2 = summation((-1)**n*x**(2*n+1)/factorial(2*n+1),(n,0,20))

Sumを使ったときと式は大体同じだが、繰り返し回数を表す文字aは消えている。
最後の(n,0,20)がその代わりとなるもので、nは0から20まで繰り返すという意味。

こうするとシグマではなくそのまま総和が展開された形の関数ができあがるようだ。

なんかすごいのが出てきてしまったが、plotするとやはりサイン波がでてる。

この関数f2(x)が本当にsinになっているのか、以下のとおりmath.sin()と比較してみた。

小数点以下9桁まで一致しているので、そこそこ精度は出ている。
繰り返し回数をもっと増やせば精度を高めることができるだろう。

以上。




。。。やるか。

ばーん!

うぉぉぉぉ!!!

オォォォォヲォォォ!

よし、完全一致。

数式でるまで30秒かかったのでまったく実用的ではないけども高精度sinが出来た。

真面目な話、繰り返し回数を増やしても浮動小数点型で表せる小数点以下の桁数に限界があるのでどこで打ち切るかが問題となる。
またSin(10)程度ならテイラー展開が精度も高くて現実的なスピードで計算できるけど、角度が増えると計算量が増えるので、C言語のライブラリでは一定角度以上はあらかじめ計算した値をライブラリ内で持っていてそれを返すという実装になってるらしい。

コード読んだわけじゃないので本当のことは知らないけど。

以上。

数式だけで絵が描ける?関数グラフアートという世界とその原理

皆さんは関数グラフアートをご存じだろうか。
その名前のとおり、数学関数のグラフを使って描かれた絵や動画などの作品を指すのだが、これまで原理が分からずもやもやしていた。

下記の記事を読んで、これがdesmosというツールで作られているということがわかり、じっさいに使ってみたところ取り急ぎ原理だけは理解できたのでご紹介。
www.sendaiikuei.ed.jp


desmos.comにアクセスするとヘッダー中央にGraphing Caluculatorというボタンがあるのでそちらをクリックすると開始できる。

サインアップもできるようだけど、特にしなくても使える。サインアップするメリットは調べてないけど、恐らく作ったグラフを保存できるとかだと推測。

早速使ってみた。

ふむ。。ただのグラフだ。こんなので絵なんて描けるのか?

半信半疑だったんだけど、調べるとxやyをこのようにして範囲指定できるらしい。

なるほど。しかしここからどうすれば。。と思い更に調べる。

あ、なるほど、いくらでも数式足せるのか。

はい、原理完璧に分かった。

もともと何か一つの複雑な数式で一筆書きのようにアートが出来上がるのかと勘違いしていたので、そんなこと出来るのか?と疑ってたんだけど、複数の数式をごちゃっとグラフ表示しているだけということなら理解できる。

数学知識があまり無い為「出来る」と「出来ない」の境界が分からず、とんでもなく間違った想定に基づいて解決策をイメージしようとして全く分からないとなる。これってプログラミング始めたばかりの方も同じようなことに陥ってるんだろうなぁと、改めて初心者の気持ちを想像できた。

さて、原理は意外とシンプルで、描きたい線により近いグラフの関数をチョイスして組み合わせるという数学センスによって、より効率的で美しいアートが出来上がるというわけだ。

人様の動画だけど、作業の雰囲気はこんな感じかな。。
youtu.be

こういうのは数学で新しい関数を覚えるきっかけにもなるし、教育的にもとても良さそうだなと思った。

さて、まるでアート用ソフトのような紹介の仕方だったので最後に訂正しておくと、desmosはれっきとした数学グラフソフトである。左上のハンバーガーメニューをクリックすると左側にサンプルを選択するメニューが出てくる。

試しにテイラー展開というのを選んでみた。

aの値をスライダーで右に動かすと紫の線が徐々に赤線に張り付いていく。

何を意味してるのかは私にはさっぱりだけど。

以上

LEGO Mindstorm EV3 ライントレースボット P制御(比例制御)をやってみた

今回はライントレースボットのP制御をやってみた。
最終的にやりたいPID制御のうち、Pの部分である。

このPはPropotional(比例しているという意味)の略である。

ライントレースボットの場合はカラーセンサーで反射値を読み取るので、その値が目標値から遠いほど操作量を増やすという処理を行う。

今回は操作量を左右旋回の際に片側のタイヤの回転数をどれくらい上げるかという係数に設定した。-1.0~1.0の範囲に収まるようにしたのでプラスの場合は左タイヤを、マイナスの場合は右タイヤを加速させ、実際の加速量は通常スピードの5倍に操作量の絶対値を掛けあわせたものとした。マイナス値をうまく扱えないか思案してみたが眠いせいか妙案が浮かばず断念。

前回の記事で紹介したライントレースボットはオンオフ制御といって目標値を上回るか下回るかしか見てなかったので直線であってとも首を振りながらの走行だったが、P制御の場合は割とまっすぐ進める。

実際に動かしてみたのがこちら。
youtube.com

おそっ!

でもP制御だけだと速度を上げた時にオーバーシュートによってカーブで簡単に脱線してしまうのでこれくらいが限界だった。
直線で加速するなど工夫すればよくなりそうだけど、オーバーシュートするというデメリットがあった方がこのあとのPI制御・PD制御のありがたみが分かりそうなので、とりあえずはこのまま次の学習に進もうと思う。

コード

import time
from pybricks.hubs import EV3Brick
from pybricks.ev3devices import (Motor, TouchSensor, ColorSensor,
                                 InfraredSensor, UltrasonicSensor, GyroSensor)
from pybricks.parameters import Port, Stop, Direction, Button, Color
from pybricks.tools import wait, StopWatch, DataLog
from pybricks.robotics import DriveBase
from pybricks.media.ev3dev import SoundFile, ImageFile

# This program requires LEGO EV3 MicroPython v2.0 or higher.
# Click "Open user guide" on the EV3 extension tab for more information.

# Create your objects here.
ev3 = EV3Brick()


# Write your program here.
ev3.speaker.beep()

a_motor = Motor(Port.A)
d_motor = Motor(Port.D)
cs = ColorSensor(Port.S1)
a_motor.reset_angle(0)
d_motor.reset_angle(0)

KP = 0.02
SPEED = 36*2

a_motor.run(SPEED)
d_motor.run(SPEED)

while True:
    u = (50.0 - cs.reflection()) * KP
    print(u)
    if u > 0:
        a_motor.run(SPEED*5*abs(u))
        d_motor.run(SPEED)
    if u <= 0:
        a_motor.run(SPEED)
        d_motor.run(SPEED*5*abs(u))

以上

LEGO Mindstorm EV3 ライントレースボット 小回りが効くようにタイヤを改良

先日作成したライントレースボットだが、小回りが効かないことによるコースアウト問題に悩まされていたが、ソースコードを直してもうまくいかず、そもそも物理的限界があるようなのでマシンを改良することにした。

まず初号機。こちらは後輪2つを前輪と同じくらいの幅で配置していたが、摩擦が大きくて全然曲がり切れない。

初回の改造では後輪を中央に寄せてしっぽを振るように稼働できるようにしたのだが、これでもまだまだ小回りは難しいことが分かった。

どうも幅の太い前輪の摩擦が足りず、滑っているようだ。

左旋回したいときに下図の摩擦Aと摩擦Bを考えてみる。

曲がりきれていないマシンを観察したところ、2つの要因を発見した。

  • 摩擦Aが不足していることによって左タイヤがその場にとどまることができずに右タイヤに引きずられて一緒に進んでしまう。
  • 摩擦Bが強すぎることによって後輪が右方向にスライドできていない。

更に摩擦Aが足りない(つまり前輪が滑る)要因として、タイヤが幅広なため荷重が分散してしまい十分な摩擦力を発揮できていないのではと考えた。

そこで今回は、細幅のタイヤを調達して後輪はボールキャスターで置き換えることにした。

完成したのがこちら。

タイヤの左右回転比を1:10にして周回動作させてみたところ、うまく小回りできている。
youtube.com
(線の周りをまわっているのはたまたま軌道が合ってるだけで、ライントレースではない。)

次にライントレースさせてみたところ、こちらもうまくいった。
youtube.com

よし、これでマシン部分の基礎は完成した。

次にセンサーを複数個つかってよりスムーズなライントレースができるように改良していこうと思う。
PID制御の実装に向けて一歩前進した。

以上。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。