社会人1年目のデカい出費を振り返る

先日預金残高が気になって通帳アプリを開いた…
 
 
 
「お金なくね?」
 
 
 
昨年4月から社会人生活を始めたしんじは, 社会人お給料パワーの全能感からこの一年間金に糸目を付けず 派手な生活を送っていたのである。

というわけで今年度最後の給料日に合わせて(日付超えたけど),社会人になってからの1年の中のとりわけデカい出費を振り返ってみようと思います。 決して浪費していたわけではなく有意義な使い方をしていたことがお分かりいただけると思います。

デカい出費 = 一万円以上くらい ただし通勤定期等の立替は除く

この一年間のデカい出費

4月

財布(13000円)

中学生のころからボロボロの財布使ってたからさすがに変えた。男前な開きやすくて薄い財布を阪急メンズで買った。

腕時計(19000円)

スマホで時間確認するのしんどすぎて買った。ヨドバシカメラの腕時計売り場数時間うろうろして悩んだ。機械式で中身の機構が見えるやつが気になったが,2万円と決めた予算と機能性からソーラーのシンプルなやつになった。

キャリーケース(14000円)

研修と帰省で移動するときに欲しくて買った。やっぱりヨドバシカメラ。今までボストンバッグ肩から下げてたのもあり,旅がすごく快適になった。ツートンカラーのデザインめちゃくちゃ気に入っている。

移動費(17000円)

GWに実家に帰る。新幹線やらなんやら。学生時代なら夜行バス一択だが社会人なので新幹線。快適すぎて感動した。

5月

移動費(17000円)

GW終わりに大阪戻る。

Northguard(10000円)

研究室の博士同期と後輩に社会人パワーを魅せるべく,卒業前に約束していたこともあり,ゲームNorthguardをギフト購入で奢った。2000円×5人分。

6月

走る靴(10000円)

ランニング意欲が急に芽生え,いい感じの走る靴を購入。土踏まずにまでフィットして,通気性良くて,軽い。

Oculus Go(30000円)

スタンドアロンHMDというところに惹かれて購入。流行に乗る意味も大きい。Netflixを寝ながら観るなどの用途に使った。今は置物。

マッチングアプリ(13000円)

彼女がどうしても欲しかったので,マッチングアプリのプレミアムプラン6か月分に課金した。ちなみに彼女は2か月でできた。

7月

移動費(17000円)

研修で地元に帰った。やっぱり新幹線。

8月

移動費(17000円)

研修が終わって大阪に戻った。

デスクトップパソコン(200000円)

ディープラーニングVR!」と騒ぎながらヨドバシカメラの店員さんと相談しながらパーツを組んだ。自作。現在ディープラーニングにもVRにも使ってないがBattle Field Vで存分にそのスペックを発揮している。

移動費(21000円)

夏季休暇に大学の友人と遊ぶため関東まで2泊3日のエクストリーム往復移動。さすがに夜行バス。ちなみに1回夜行バスを逃して3泊4日に。さらに翌日ではなく翌々日のバスを誤って予約してしまい4泊5日に。21000円に含まれない宿泊費は計算したくもない。東京のネカフェは高い。

9月

秋冬服(25000円)

いい感じの私服。今も着てる。

カジュアル靴(10000円)

いい感じの私服に合わせたコンバースの靴。現在そこそこぼろぼろ。

10月

淡路島旅行(20000円)

彼女と旅行。レンタカーで一周。飯食ったり景色見たり。

RICHO THETA SC(21000円)

言わずと知れた360°カメラ。旅行と飲み会でよく使う。旅行先の景色が写せるのもいいけど,個人的には飲み会でテーブル囲んでる全員の顔と料理を全部写せるのがすごくお気に入り。かなりいい買い物。加工アプリがあるので,一部切り出して矩形画像にしたり,切り出し範囲を動かして動画にしたり,映りこんだ他人の顔を隠したりと使い勝手が良い。LINEにはアプリから直接投稿ができる。

11月

北海道旅行(60000円)

彼女と旅行。飛行機代も宿も安かった。

結婚式(140000円)

兄の結婚式。 移動費,礼服,ご祝儀,親戚との飲み代,諸々含めるとこのくらい。礼服は7万円くらいしたけど大事な時にずっと使うものなのでまあOK。兄,おめでとう。

語学研修(17000円)

会社の英会話教室。費用は折半。自分への投資ということで。

12月

Nintendo Switch(30000円)

ボーナスとスマブラSP発売が重なって即購入。このあたりSwitch人口めっちゃ増えたと思う。ヨドバシカメラの地下2階も大行列だった。合わせて買ったソフトはスマブラSPゼルダの伝説BoW。買って以降土曜日がよく溶けるようになった。

オフィスカジュアル(37000円)

オフィスカジュアル専門店みたいなところでいい感じのシャツといい感じのジャケットを購入。毎日着てる。ジャケットが高価だけどシャツもそれなりに高くて買ってから落ち込んだ記憶がある。

GoPro(45000円)

PayPayチャレンジでとりあえず買った。スノボとサバゲ用。THETAと比べると驚くほど画質がよくてびっくり。アクションカメラである以上にそもそもカメラとして高性能。

ディスプレイ(20000円)

PayPayチャレンジでとりあえず買った。2枚目のディスプレイ。(1枚目は前記デスクトップパソコンと一緒に買った。) 1枚目より少し大きめでメインディスプレイとなった。

Kindle Paperwhite(12000円)

言わずと知れた電子書籍端末。通勤時と寝る前のスマホいじる時間がKindleでの読書の時間に変わった。技術系の読み物と漫画が中心。TOEICのリーディング対策もKindleでやってる。

新幹線(17000円)

年末の帰省。

1月

新幹線(17000円)

帰省から戻る。

コート(20000円)

職場にもカジュアルにも使える冬用のコートが欲しくて買った。店は結婚式の礼服買った場所と同じ。長い裾と腰のベルトと袖のベルトが欲しかったが,よほど高いコートじゃないと全て満たさない。ちょっと妥協して袖のベルトは諦めた。

スノボウェア(85000円)

ゴーグル,グローブ込み。相場は3万円程度と知っていたが,いい売り場を知らず紫色の専門店にたどり着いてしまった。QUIKSILVERというブランド。デザインがかっこよく,水が絶対にしみない,スノボ初心者にはもったいないハイグレードなウェア。数十回着ないと元が取れない。

2月

フェスチケット(10000円)

5月のMETROCK2019 OSAKAのチケット。KANA-BOON目当て。

3月

北海道旅行(120000円)

彼女と別れて寮の友人と北海道旅行。札幌とすすきの周辺で3日間豪遊の限りを尽くしたので前回の北海道旅行より高め。

元とれた買い物

www.amazon.co.jp

THETA はそこそこ色んなイベントで振り回してて,SNS投稿も捗るので買って大満足。スマホでは撮れない良さがある。いつも持ち歩くようにしたい。

www.amazon.co.jp

Kindle も十分すぎるほど日々の生活に浸透している。コートの左ポケットに常に入れていて暇さえあれば読書している。通勤時も寝る前も,通勤時に物足りなければ適当なカフェによって読み続けたりしてる。そうして得られるものが楽しみだけでなくて知識だったり教養だったり英語力だったりするので12000円は安すぎるかなと思う。

無駄遣いな買い物

スノボウェアはさすがに高すぎた。ウェア自体に惹かれた気持ちもあるけど,どちらかといえば試着もしたしすごく欲しそうにしてしまった手前店員さんの前から逃げられなくなったのが大きい。数万円の出費なので決断を急がず「いったん考えます」で店を出るくらいはやらないとまずい。今回は梅田の紫の専門店で購入したが,電車でアクセスできない少し離れたスポーツ用品店は品ぞろえも豊富で価格帯も広かった。こっちで買うべきだったと思う。
スノボウェアに限らず,服も無理に梅田周辺で買う必要ない。ユニクロ,GU,もしくはもっと都市部から離れた手ごろな店を探しておこう。
社会人になってから関東へは基本新幹線だったが,長期休暇なら夜行バスで十分な気がする。  
 
一応「1年目は貯金しない」という計画があったので,いつか必要になるものはひたすら買っていったわけですが,買う場所と買い方には気を付けないといけないですね。
 
結論「なんでも梅田駅周辺で買い物を済ませない」
 
4月から新入社員になる方もいると思いますが僕の1年目はこんな感じでした。

M5Stack GRAY 買って最小限のコードで加速度センサを試した

M5Stack 買った

第二の産業革命を起こすとも言われているM5 Stack を購入しました。

www.m5stack.com

購入のきっかけになったツイートです。

M5Stackがどんなものかというと,ESP32というArduino IDEでコード書けるマイコンに,電子工作で使いそうな基本的なものだいたい全部乗せみたいなやつです。 液晶と3つのボタンと5×5のガワが印象的で見た目もスマート。

Stackって名前だけにモジュール(Arduino シールド基板みたいなやつ?)を重ねて積めるらしいです。

細かい解説は上のリンクに譲って,個人的に魅力的だと思ったのは,

  • LCD(320×240の液晶)ついてて描画も簡単
  • ESP32がWiFiモジュール積んでる
  • SDカードのスロットついててmp3の音声流せる
  • Bluetoothも行けそう
  • 磁石ついててホワイトボードとか玄関のドアに貼り付けられる
  • バッテリーも標準装備でケーブル切り離してもしばらくOK

です。

特にちょっとした解像度の液晶と無線通信と音声再生は今までのArduino工作の経験からちょっとハードルが高かったので, その辺が簡単にできそうなのはうれしい。(まだやってないけど)

M5Stack GRAY でIMUを使う

M5Stackにもバリエーションがあって,今回はBASICとGRAYを買いました。 BASICに9軸IMU(加速度,ジャイロ,磁気センサ)と温度センサ積んだのがGRAYです。

サンプルスケッチを見る

ひとまず重力加速度からボールコロコロでもしたいなと思って,加速度センサ使ってそうなサンプルスケッチを見ました。

/* MPU9250 Basic Example Code
 by: Kris Winer
 date: April 1, 2014
 license: Beerware - Use this code however you'd like. If you
 find it useful you can buy me a beer some time.
 Modified by Brent Wilkins July 19, 2016

 Demonstrate basic MPU-9250 functionality including parameterizing the register
 addresses, initializing the sensor, getting properly scaled accelerometer,
 gyroscope, and magnetometer data out. Added display functions to allow display
 to on breadboard monitor. Addition of 9 DoF sensor fusion using open source
 Madgwick and Mahony filter algorithms. Sketch runs on the 3.3 V 8 MHz Pro Mini
 and the Teensy 3.1.
 */

#include <M5Stack.h>
#include "utility/MPU9250.h"
#include "utility/quaternionFilters.h"

#define processing_out false
#define AHRS true         // Set to false for basic data read
#define SerialDebug true  // Set to true to get Serial output for debugging
#define LCD

MPU9250 IMU;
// Kalman kalmanX, kalmanY, kalmanZ; // Create the Kalman instances

void setup()
{
  M5.begin();
  Wire.begin();

#ifdef LCD
  // Start device display with ID of sensor
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(WHITE ,BLACK); // Set pixel color; 1 on the monochrome screen
  M5.Lcd.setTextSize(2);
  M5.Lcd.setCursor(0,0); M5.Lcd.print("MPU9250");
  M5.Lcd.setTextSize(1);
  M5.Lcd.setCursor(0, 20); M5.Lcd.print("9-DOF 16-bit");
  M5.Lcd.setCursor(0, 30); M5.Lcd.print("motion sensor");
  M5.Lcd.setCursor(20,40); M5.Lcd.print("60 ug LSB");
  delay(1000);

  // Set up for data display
  M5.Lcd.setTextSize(1); // Set text size to normal, 2 is twice normal etc.
  M5.Lcd.fillScreen(BLACK);   // clears the screen and buffer
#endif // LCD

  // Read the WHO_AM_I register, this is a good test of communication
  byte c = IMU.readByte(MPU9250_ADDRESS, WHO_AM_I_MPU9250);
  Serial.print("MPU9250 "); Serial.print("I AM "); Serial.print(c, HEX);
  Serial.print(" I should be "); Serial.println(0x71, HEX);

#ifdef LCD
  M5.Lcd.setCursor(20,0); M5.Lcd.print("MPU9250");
  M5.Lcd.setCursor(0,10); M5.Lcd.print("I AM");
  M5.Lcd.setCursor(0,20); M5.Lcd.print(c, HEX);
  M5.Lcd.setCursor(0,30); M5.Lcd.print("I Should Be");
  M5.Lcd.setCursor(0,40); M5.Lcd.print(0x71, HEX);
  delay(1000);
#endif // LCD

  // if (c == 0x71) // WHO_AM_I should always be 0x68
  {
    Serial.println("MPU9250 is online...");

    // Start by performing self test and reporting values
    IMU.MPU9250SelfTest(IMU.SelfTest);
    Serial.print("x-axis self test: acceleration trim within : ");
    Serial.print(IMU.SelfTest[0],1); Serial.println("% of factory value");
    Serial.print("y-axis self test: acceleration trim within : ");
    Serial.print(IMU.SelfTest[1],1); Serial.println("% of factory value");
    Serial.print("z-axis self test: acceleration trim within : ");
    Serial.print(IMU.SelfTest[2],1); Serial.println("% of factory value");
    Serial.print("x-axis self test: gyration trim within : ");
    Serial.print(IMU.SelfTest[3],1); Serial.println("% of factory value");
    Serial.print("y-axis self test: gyration trim within : ");
    Serial.print(IMU.SelfTest[4],1); Serial.println("% of factory value");
    Serial.print("z-axis self test: gyration trim within : ");
    Serial.print(IMU.SelfTest[5],1); Serial.println("% of factory value");

    // Calibrate gyro and accelerometers, load biases in bias registers
    IMU.calibrateMPU9250(IMU.gyroBias, IMU.accelBias);

#ifdef LCD
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setTextSize(1);
    M5.Lcd.setCursor(0, 0); M5.Lcd.print("MPU9250 bias");
    M5.Lcd.setCursor(0, 16); M5.Lcd.print(" x   y   z  ");

    M5.Lcd.setCursor(0,  32); M5.Lcd.print((int)(1000*IMU.accelBias[0]));
    M5.Lcd.setCursor(32, 32); M5.Lcd.print((int)(1000*IMU.accelBias[1]));
    M5.Lcd.setCursor(64, 32); M5.Lcd.print((int)(1000*IMU.accelBias[2]));
    M5.Lcd.setCursor(96, 32); M5.Lcd.print("mg");

    M5.Lcd.setCursor(0,  48); M5.Lcd.print(IMU.gyroBias[0], 1);
    M5.Lcd.setCursor(32, 48); M5.Lcd.print(IMU.gyroBias[1], 1);
    M5.Lcd.setCursor(64, 48); M5.Lcd.print(IMU.gyroBias[2], 1);
    M5.Lcd.setCursor(96, 48); M5.Lcd.print("o/s");
    delay(1000);
#endif // LCD

    IMU.initMPU9250();
    // Initialize device for active mode read of acclerometer, gyroscope, and
    // temperature
    Serial.println("MPU9250 initialized for active data mode....");

    // Read the WHO_AM_I register of the magnetometer, this is a good test of
    // communication
    byte d = IMU.readByte(AK8963_ADDRESS, WHO_AM_I_AK8963);
    Serial.print("AK8963 "); Serial.print("I AM "); Serial.print(d, HEX);
    Serial.print(" I should be "); Serial.println(0x48, HEX);

#ifdef LCD
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(20,0); M5.Lcd.print("AK8963");
    M5.Lcd.setCursor(0,10); M5.Lcd.print("I AM");
    M5.Lcd.setCursor(0,20); M5.Lcd.print(d, HEX);
    M5.Lcd.setCursor(0,30); M5.Lcd.print("I Should Be");
    M5.Lcd.setCursor(0,40); M5.Lcd.print(0x48, HEX);
    delay(1000);
#endif // LCD

    // Get magnetometer calibration from AK8963 ROM
    IMU.initAK8963(IMU.magCalibration);
    // Initialize device for active mode read of magnetometer
    Serial.println("AK8963 initialized for active data mode....");
    if (Serial)
    {
      //  Serial.println("Calibration values: ");
      Serial.print("X-Axis sensitivity adjustment value ");
      Serial.println(IMU.magCalibration[0], 2);
      Serial.print("Y-Axis sensitivity adjustment value ");
      Serial.println(IMU.magCalibration[1], 2);
      Serial.print("Z-Axis sensitivity adjustment value ");
      Serial.println(IMU.magCalibration[2], 2);
    }

#ifdef LCD
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(20,0); M5.Lcd.print("AK8963");
    M5.Lcd.setCursor(0,10); M5.Lcd.print("ASAX "); M5.Lcd.setCursor(50,10);
    M5.Lcd.print(IMU.magCalibration[0], 2);
    M5.Lcd.setCursor(0,20); M5.Lcd.print("ASAY "); M5.Lcd.setCursor(50,20);
    M5.Lcd.print(IMU.magCalibration[1], 2);
    M5.Lcd.setCursor(0,30); M5.Lcd.print("ASAZ "); M5.Lcd.setCursor(50,30);
    M5.Lcd.print(IMU.magCalibration[2], 2);
    delay(1000);
    #endif // LCD
  } // if (c == 0x71)
  // else
  // {
  //   Serial.print("Could not connect to MPU9250: 0x");
  //   Serial.println(c, HEX);
  //   while(1) ; // Loop forever if communication doesn't happen
  // }

  M5.Lcd.setTextSize(1);
  M5.Lcd.setTextColor(GREEN ,BLACK);
  M5.Lcd.fillScreen(BLACK);
}

void loop()
{
  // If intPin goes high, all data registers have new data
  // On interrupt, check if data ready interrupt
  if (IMU.readByte(MPU9250_ADDRESS, INT_STATUS) & 0x01)
  {  
    IMU.readAccelData(IMU.accelCount);  // Read the x/y/z adc values
    IMU.getAres();

    // Now we'll calculate the accleration value into actual g's
    // This depends on scale being set
    IMU.ax = (float)IMU.accelCount[0]*IMU.aRes; // - accelBias[0];
    IMU.ay = (float)IMU.accelCount[1]*IMU.aRes; // - accelBias[1];
    IMU.az = (float)IMU.accelCount[2]*IMU.aRes; // - accelBias[2];

    IMU.readGyroData(IMU.gyroCount);  // Read the x/y/z adc values
    IMU.getGres();

    // Calculate the gyro value into actual degrees per second
    // This depends on scale being set
    IMU.gx = (float)IMU.gyroCount[0]*IMU.gRes;
    IMU.gy = (float)IMU.gyroCount[1]*IMU.gRes;
    IMU.gz = (float)IMU.gyroCount[2]*IMU.gRes;

    IMU.readMagData(IMU.magCount);  // Read the x/y/z adc values
    IMU.getMres();
    // User environmental x-axis correction in milliGauss, should be
    // automatically calculated
    IMU.magbias[0] = +470.;
    // User environmental x-axis correction in milliGauss TODO axis??
    IMU.magbias[1] = +120.;
    // User environmental x-axis correction in milliGauss
    IMU.magbias[2] = +125.;

    // Calculate the magnetometer values in milliGauss
    // Include factory calibration per data sheet and user environmental
    // corrections
    // Get actual magnetometer value, this depends on scale being set
    IMU.mx = (float)IMU.magCount[0]*IMU.mRes*IMU.magCalibration[0] -
               IMU.magbias[0];
    IMU.my = (float)IMU.magCount[1]*IMU.mRes*IMU.magCalibration[1] -
               IMU.magbias[1];
    IMU.mz = (float)IMU.magCount[2]*IMU.mRes*IMU.magCalibration[2] -
               IMU.magbias[2];
  } // if (readByte(MPU9250_ADDRESS, INT_STATUS) & 0x01)

  // Must be called before updating quaternions!
  IMU.updateTime();

  // Sensors x (y)-axis of the accelerometer is aligned with the y (x)-axis of
  // the magnetometer; the magnetometer z-axis (+ down) is opposite to z-axis
  // (+ up) of accelerometer and gyro! We have to make some allowance for this
  // orientationmismatch in feeding the output to the quaternion filter. For the
  // MPU-9250, we have chosen a magnetic rotation that keeps the sensor forward
  // along the x-axis just like in the LSM9DS0 sensor. This rotation can be
  // modified to allow any convenient orientation convention. This is ok by
  // aircraft orientation standards! Pass gyro rate as rad/s
//  MadgwickQuaternionUpdate(ax, ay, az, gx*PI/180.0f, gy*PI/180.0f, gz*PI/180.0f,  my,  mx, mz);
  MahonyQuaternionUpdate(IMU.ax, IMU.ay, IMU.az, IMU.gx*DEG_TO_RAD,
                         IMU.gy*DEG_TO_RAD, IMU.gz*DEG_TO_RAD, IMU.my,
                         IMU.mx, IMU.mz, IMU.deltat);

  if (!AHRS)
  {
    IMU.delt_t = millis() - IMU.count;
    if (IMU.delt_t > 500)
    {
      if(SerialDebug)
      {
        // Print acceleration values in milligs!
        Serial.print("X-acceleration: "); Serial.print(1000*IMU.ax);
        Serial.print(" mg ");
        Serial.print("Y-acceleration: "); Serial.print(1000*IMU.ay);
        Serial.print(" mg ");
        Serial.print("Z-acceleration: "); Serial.print(1000*IMU.az);
        Serial.println(" mg ");

        // Print gyro values in degree/sec
        Serial.print("X-gyro rate: "); Serial.print(IMU.gx, 3);
        Serial.print(" degrees/sec ");
        Serial.print("Y-gyro rate: "); Serial.print(IMU.gy, 3);
        Serial.print(" degrees/sec ");
        Serial.print("Z-gyro rate: "); Serial.print(IMU.gz, 3);
        Serial.println(" degrees/sec");

        // Print mag values in degree/sec
        Serial.print("X-mag field: "); Serial.print(IMU.mx);
        Serial.print(" mG ");
        Serial.print("Y-mag field: "); Serial.print(IMU.my);
        Serial.print(" mG ");
        Serial.print("Z-mag field: "); Serial.print(IMU.mz);
        Serial.println(" mG");

        IMU.tempCount = IMU.readTempData();  // Read the adc values
        // Temperature in degrees Centigrade
        IMU.temperature = ((float) IMU.tempCount) / 333.87 + 21.0;
        // Print temperature in degrees Centigrade
        Serial.print("Temperature is ");  Serial.print(IMU.temperature, 1);
        Serial.println(" degrees C");
        Serial.println("");
      }

#ifdef LCD
      M5.Lcd.fillScreen(BLACK);
      M5.Lcd.setTextColor(GREEN ,BLACK);
      M5.Lcd.setCursor(0, 0); M5.Lcd.print("MPU9250/AK8963");
      M5.Lcd.setCursor(0, 32); M5.Lcd.print(" x   y   z  ");

      M5.Lcd.setCursor(0,  48); M5.Lcd.print((int)(1000*IMU.ax));
      M5.Lcd.setCursor(32, 48); M5.Lcd.print((int)(1000*IMU.ay));
      M5.Lcd.setCursor(64, 48); M5.Lcd.print((int)(1000*IMU.az));
      M5.Lcd.setCursor(96, 48); M5.Lcd.print("mg");

      M5.Lcd.setCursor(0,  64); M5.Lcd.print((int)(IMU.gx));
      M5.Lcd.setCursor(32, 64); M5.Lcd.print((int)(IMU.gy));
      M5.Lcd.setCursor(64, 64); M5.Lcd.print((int)(IMU.gz));
      M5.Lcd.setCursor(96, 64); M5.Lcd.print("o/s");

      M5.Lcd.setCursor(0,  96); M5.Lcd.print((int)(IMU.mx));
      M5.Lcd.setCursor(32, 96); M5.Lcd.print((int)(IMU.my));
      M5.Lcd.setCursor(64, 96); M5.Lcd.print((int)(IMU.mz));
      M5.Lcd.setCursor(96, 96); M5.Lcd.print("mG");

      M5.Lcd.setCursor(0,  128); M5.Lcd.print("Gyro T ");
      M5.Lcd.setCursor(50,  128); M5.Lcd.print(IMU.temperature, 1);
      M5.Lcd.print(" C");
#endif // LCD

      IMU.count = millis();
      // digitalWrite(myLed, !digitalRead(myLed));  // toggle led
    } // if (IMU.delt_t > 500)
  } // if (!AHRS)
  else
  {
    // Serial print and/or display at 0.5 s rate independent of data rates
    IMU.delt_t = millis() - IMU.count;

    // update LCD once per half-second independent of read rate
    // if (IMU.delt_t > 500)
    if (IMU.delt_t > 100)
    {
      if(SerialDebug)
      {
        Serial.print("ax = "); Serial.print((int)1000*IMU.ax);
        Serial.print(" ay = "); Serial.print((int)1000*IMU.ay);
        Serial.print(" az = "); Serial.print((int)1000*IMU.az);
        Serial.println(" mg");

        Serial.print("gx = "); Serial.print( IMU.gx, 2);
        Serial.print(" gy = "); Serial.print( IMU.gy, 2);
        Serial.print(" gz = "); Serial.print( IMU.gz, 2);
        Serial.println(" deg/s");

        Serial.print("mx = "); Serial.print( (int)IMU.mx );
        Serial.print(" my = "); Serial.print( (int)IMU.my );
        Serial.print(" mz = "); Serial.print( (int)IMU.mz );
        Serial.println(" mG");

        Serial.print("q0 = "); Serial.print(*getQ());
        Serial.print(" qx = "); Serial.print(*(getQ() + 1));
        Serial.print(" qy = "); Serial.print(*(getQ() + 2));
        Serial.print(" qz = "); Serial.println(*(getQ() + 3));
      }

// Define output variables from updated quaternion---these are Tait-Bryan
// angles, commonly used in aircraft orientation. In this coordinate system,
// the positive z-axis is down toward Earth. Yaw is the angle between Sensor
// x-axis and Earth magnetic North (or true North if corrected for local
// declination, looking down on the sensor positive yaw is counterclockwise.
// Pitch is angle between sensor x-axis and Earth ground plane, toward the
// Earth is positive, up toward the sky is negative. Roll is angle between
// sensor y-axis and Earth ground plane, y-axis up is positive roll. These
// arise from the definition of the homogeneous rotation matrix constructed
// from quaternions. Tait-Bryan angles as well as Euler angles are
// non-commutative; that is, the get the correct orientation the rotations
// must be applied in the correct order which for this configuration is yaw,
// pitch, and then roll.
// For more see
// http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
// which has additional links.
      IMU.yaw   = atan2(2.0f * (*(getQ()+1) * *(getQ()+2) + *getQ() *
                    *(getQ()+3)), *getQ() * *getQ() + *(getQ()+1) * *(getQ()+1)
                    - *(getQ()+2) * *(getQ()+2) - *(getQ()+3) * *(getQ()+3));
      IMU.pitch = -asin(2.0f * (*(getQ()+1) * *(getQ()+3) - *getQ() *
                    *(getQ()+2)));
      IMU.roll  = atan2(2.0f * (*getQ() * *(getQ()+1) + *(getQ()+2) *
                    *(getQ()+3)), *getQ() * *getQ() - *(getQ()+1) * *(getQ()+1)
                    - *(getQ()+2) * *(getQ()+2) + *(getQ()+3) * *(getQ()+3));
      IMU.pitch *= RAD_TO_DEG;
      IMU.yaw   *= RAD_TO_DEG;
      // Declination of SparkFun Electronics (40°05'26.6"N 105°11'05.9"W) is
      //   8° 30' E  ± 0° 21' (or 8.5°) on 2016-07-19
      // - http://www.ngdc.noaa.gov/geomag-web/#declination
      IMU.yaw   -= 8.5;
      IMU.roll  *= RAD_TO_DEG;

      if(SerialDebug)
      {
        Serial.print("Yaw, Pitch, Roll: ");
        Serial.print(IMU.yaw, 2);
        Serial.print(", ");
        Serial.print(IMU.pitch, 2);
        Serial.print(", ");
        Serial.println(IMU.roll, 2);

        Serial.print("rate = ");
        Serial.print((float)IMU.sumCount/IMU.sum, 2);
        Serial.println(" Hz");
        Serial.println("");
      }

#ifdef LCD
      // M5.Lcd.fillScreen(BLACK);
      M5.Lcd.setTextFont(2);

      M5.Lcd.setCursor(0, 0); M5.Lcd.print("     x       y       z ");
      M5.Lcd.setCursor(0,  24);
      M5.Lcd.printf("% 6d  % 6d  % 6d     mg   \r\n",  (int)(1000*IMU.ax), (int)(1000*IMU.ay), (int)(1000*IMU.az));
      M5.Lcd.setCursor(0,  44);
      M5.Lcd.printf("% 6d  % 6d  % 6d      o/s  \r\n", (int)(IMU.gx), (int)(IMU.gy), (int)(IMU.gz));
      M5.Lcd.setCursor(0,  64);
      M5.Lcd.printf("% 6d  % 6d  % 6d     mG    \r\n",  (int)(IMU.mx), (int)(IMU.my), (int)(IMU.mz));
  
      M5.Lcd.setCursor(0,  100);
      M5.Lcd.printf("  yaw: % 5.2f    pitch: % 5.2f    roll: % 5.2f   \r\n",(IMU.yaw), (IMU.pitch), (IMU.roll));

    // With these settings the filter is updating at a ~145 Hz rate using the
    // Madgwick scheme and >200 Hz using the Mahony scheme even though the
    // display refreshes at only 2 Hz. The filter update rate is determined
    // mostly by the mathematical steps in the respective algorithms, the
    // processor speed (8 MHz for the 3.3V Pro Mini), and the magnetometer ODR:
    // an ODR of 10 Hz for the magnetometer produce the above rates, maximum
    // magnetometer ODR of 100 Hz produces filter update rates of 36 - 145 and
    // ~38 Hz for the Madgwick and Mahony schemes, respectively. This is
    // presumably because the magnetometer read takes longer than the gyro or
    // accelerometer reads. This filter update rate should be fast enough to
    // maintain accurate platform orientation for stabilization control of a
    // fast-moving robot or quadcopter. Compare to the update rate of 200 Hz
    // produced by the on-board Digital Motion Processor of Invensense's MPU6050
    // 6 DoF and MPU9150 9DoF sensors. The 3.3 V 8 MHz Pro Mini is doing pretty
    // well!

      // M5.Lcd.setCursor(0, 60);
      // M5.Lcd.printf("yaw:%6.2f   pitch:%6.2f   roll:%6.2f  ypr \r\n",(IMU.yaw), (IMU.pitch), (IMU.roll));
      M5.Lcd.setCursor(12, 144); 
      M5.Lcd.print("rt: ");
      M5.Lcd.print((float) IMU.sumCount / IMU.sum, 2);
      M5.Lcd.print(" Hz");
#endif // LCD

      IMU.count = millis();
      IMU.sumCount = 0;
      IMU.sum = 0;

#if(processing_out)

      Serial.print(((IMU.yaw)));    Serial.print(";");
      Serial.print(((IMU.pitch))); Serial.print(";");
      Serial.print(((IMU.roll)));   Serial.print(";");
      Serial.print(26.5);    Serial.print(";");
      Serial.print(0.01);    Serial.print(";");
      Serial.print(0.02);    Serial.println();
#endif
    } // if (IMU.delt_t > 500)
  } // if (AHRS)
}

長い!!!!

挫折しかけたわ!!!!危ねぇ!!!!

サンプルで一通り機能見せたり,表示したいものも多いので,色々盛り盛りになっているのだと思います。

必要なとこだけ抜き取る

とりあえず,加速度三軸の値だけ取ってこれればいいやという気持ちなので, 必要そうなところだけ抜き出してみました。 通信相手のアドレスの確認,他のセンサ(ジャイロと磁気)の計測,printの部分を無視して取り出すと。

#include <M5Stack.h>
#include "utility/MPU9250.h"

MPU9250 IMU;

void setup() {
  M5.begin();
  //  serial for debugging
  Serial.begin(115200);
  //  i2c as a master
  Wire.begin();
  IMU.initMPU9250(); 

}

void loop() {

  if (IMU.readByte(MPU9250_ADDRESS, INT_STATUS) & 0x01)
  {  
    IMU.readAccelData(IMU.accelCount);  // Read the x/y/z adc values
    IMU.getAres();

    // Now we'll calculate the accleration value into actual g's
    // This depends on scale being set
    IMU.ax = (float)IMU.accelCount[0]*IMU.aRes; // - accelBias[0];
    IMU.ay = (float)IMU.accelCount[1]*IMU.aRes; // - accelBias[1];
    IMU.az = (float)IMU.accelCount[2]*IMU.aRes; // - accelBias[2];
    //あとは 加速度3軸分のfloatデータ IMU.ax, IMU.ay, IMU.az でお好きに…
}

だいぶすっきりした。

やっているのは,

ライブラリを用意。

#include "utility/MPU9250.h"

IMUのインスタンスを作る。

MPU9250 IMU;

通信を始める。

  Wire.begin();

IMUを初期化する。

  IMU.initMPU9250(); 

データを読む。

  if (IMU.readByte(MPU9250_ADDRESS, INT_STATUS) & 0x01)
  {  
    IMU.readAccelData(IMU.accelCount);  // Read the x/y/z adc values
    IMU.getAres();

    // Now we'll calculate the accleration value into actual g's
    // This depends on scale being set
    IMU.ax = (float)IMU.accelCount[0]*IMU.aRes; // - accelBias[0];
    IMU.ay = (float)IMU.accelCount[1]*IMU.aRes; // - accelBias[1];
    IMU.az = (float)IMU.accelCount[2]*IMU.aRes; // - accelBias[2];
    //あとは 加速度3軸分のfloatデータ IMU.ax, IMU.ay, IMU.az でお好きに…
}

…です。

ボールコロコロさせた

加速度取る最小限のコードが分かったので,ボールコロコロさせました。

楽しい!

以下,スケッチです。

#include <M5Stack.h>
#include "utility/MPU9250.h"

MPU9250 IMU;

//ボールの初期位置,初速度
float posx = 160;
float posy = 120;
float velx = 0;
float vely = 0;
int radius = 5; //描画するボールの半径

//ループ一回分の時間とそれを計算するためのタイマーです。
unsigned int dt = 0;
unsigned int timer = millis();


void setup() {
  M5.begin();
  //  serial for debugging
  Serial.begin(115200);
  //  i2c as a master
  Wire.begin();
  IMU.initMPU9250();
  

}

void loop() {

  if (IMU.readByte(MPU9250_ADDRESS, INT_STATUS) & 0x01)
  {  
    IMU.readAccelData(IMU.accelCount);  // Read the x/y/z adc values
    IMU.getAres();

    // Now we'll calculate the accleration value into actual g's
    // This depends on scale being set
    IMU.ax = (float)IMU.accelCount[0]*IMU.aRes; // - accelBias[0];
    IMU.ay = (float)IMU.accelCount[1]*IMU.aRes; // - accelBias[1];
    IMU.az = (float)IMU.accelCount[2]*IMU.aRes; // - accelBias[2];

    //これはPC側のシリアルモニタで見ます。デバッグ用です。
    Serial.print(timer);
    Serial.print(' ');
    Serial.print(dt);
    Serial.print(' ');
    Serial.print(IMU.ax);
    Serial.print(' ');
    Serial.println(IMU.ay);

    M5.Lcd.fillScreen(BLACK);
  
    dt = millis() - timer;
    float dtf = (float)dt/1000; //運動の計算用に使うものは単位[s]にしときます。
    //速度更新
    velx = velx + -500 * IMU.ax * dtf; //500は適当に調整しました。慣性を決めます。
    vely = vely + 500 * IMU.ay * dtf;

    //位置更新
    posx = posx + velx * dtf;
    posy = posy + vely * dtf;
    timer = millis();

    //はみ出さない,速くしすぎない
    posx = constrain(posx, 0, 319);
    posy = constrain(posy, 0, 239);
    velx = constrain(velx, -319, 319);
    vely = constrain(vely, -239, 239);
    
    //端っこ来たら跳ね返る
    if(posx == 0 && velx < 0)
      velx = velx * -0.8;
    if(posx == 319 && velx > 0)
      velx = velx * -0.8;
    if(posy == 0 && vely < 0)
      vely = vely * -0.8;
    if(posy == 239 && vely > 0)
      vely = vely * -0.8;
    
    //ボールの描画
    M5.Lcd.fillCircle((int)posx, (int)posy, radius, RED);
    
  }
  delay(50);
}

今回はひとまず加速度の取得だけ簡単に試してみました。

せっかくなので今まであまり踏み込んでこなかった無線通信で連携させてやって遊んでみたいですね。 2つ買ったので。

ちなみにmicro:bit君ですが諸事情あって積み基板になりました。

それではまた。

慣れないスノボでいきなりGoProデビューした

スノボに行きました。

スノボは研究室で2回旅行に行った程度で,ビギナーといえばビギナー,しかし木の葉滑り位はできるという感じ。今年こそは人並みにできるようになってエンジョイ勢になりたいなという気持ちで当日を楽しみにしていました。

楽しみはもう一つあって,それは GoPro HERO7 Black での動画撮影。

gopro.com


昨年の PayPayの100億円あげちゃうキャンペーン で「とりあえずいつか買いたかったもの買っとけ!」とばかりに買っちゃいました。ヘッドストラップもセットです。目線動画撮るぞ!

スノボで使ってみて最高にいい買い物だなと思う一方,家に帰って動画を見直す段になってやってしまったなと思うことがあったのでここに書いておきます。

 

最高にいい買い物だな

普段撮れないダイナミックな動画が取れること,自身が体感したスノボの楽しさを後から振り返れること,ですかね。スノボとかしょっちゅう行けるものではないですし,一度の体験を何度も味わいたいものです。

そのうえ画質(4k)も良いので動画から写真切り出ししても十分なクオリティ。(今回はデフォルト1920x1440でしたが。) いいシーンはどんどん写真切り出しして,一緒に遊んだ友人に共有してしまおう。

 

やってしまったな

動画のスタートとストップを取り違えるミスが多発したことです。

音声コントロール

スノボエンジョイ中のGoPro操作はほとんど音声コントロールでしています。すごい!
滑走中は手を離せないし,そうでなくても手には分厚い手袋,カメラは額前に取り付けなのでそう簡単に手に持って操作できません。(ゴーグルとニットの兼ね合いもあってめんどくさい)

というわけで動画撮影も電源のON/OFFも音声で行いました。

GoPro HERO7 Blackで使える音声コマンドは以下の通り(説明書から引用)

  • GoPro ビデオ スタート:ビデオの録画を開始します。
  • GoPro ハイライト:撮影中に HiLight タグを追加します。
  • GoPro ビデオ ストップ:ビデオ撮影を停止します。
  • GoPro 写真:写真を 1 枚撮影します。
  • GoPro バースト:連写で写真を撮影します。
  • GoPro タイム ラプス スタート:タイム ラプス フォトの撮影を開始し
    ます。
  • GoPro タイム ラプス ストップ:タイム ラプス フォトの撮影を停止し
    ます。
  • GoPro 電源オン:カメラの電源をオンにします (音声起動
    をオンにしておく必要があります)。
  • GoPro 電源オフ:カメラの電源をオフにします。
  • 以下略…

「Alexa 今日の天気教えて」みたいなノリですね。今回使ったのは「GoPro ビデオ スタート」,「GoPro ビデオ ストップ」,「GoPro 電源オン」,「GoPro 電源オフ」の4つです。ビデオスタート/ストップは当然動画撮影をするため。電源オン/オフはバッテリー節約が目的です。ただでさえ1440p60の設定で70~75分しか持たないところを,メインディスプレイつけっぱなしだとすぐなくなりそうなのでこまめにオンオフしてました。

頭につけてるとGoProの状態が分からない

頭に取り付けされていて,外して確認してまた付けるのも一苦労。なので,GoProがどんな状態なのか,電源はついているのかどうか,動画は撮影中なのかスタンバイ中なのか分かりませんでした。音声コントロールするにもこれだとすごく不安。これは一応,友人にフロントディスプレイ(メインディスプレイとは別の電卓みたいな液晶でバッテリー残量,動画撮影時間等が示されている)を見てもらうことや,それをスマホ画面の反射で見るなどして解決しました。手鏡とか持って画面が確認できると宇宙飛行士っぽくていいかも。(宇宙服の装備品および船外活動用工具

f:id:shinji00:20190203215245p:plain

フロントディスプレイ ここに電池残量や撮影状況などリアルタイムに気になる情報がある

音声認識が通ったかは電子音で分かります。電源オンなら電子音がピピピと3回。オフなら7回ピピピピピピピ。ビデオスタートは一回だけピ。ビデオストップは一度だけピと反応してからピピピと3回。一応スノボに持っていく前にテストとしてやってみたのですが結局すべて覚えきれず,「まあ音なったら大丈夫やろ」位の認識で挑みました。だから音声を出した後のピが,撮影開始のものか終了のものか区別つかなかったのです。

状態を確認するコマンドとかあったらいいのにな。でもそのフィードバックがまた電子音だったら分からないかも…

ビデオスタートとビデオストップの誤認識

家に帰って動画を確認したらこんな動画がありました

「動画撮影が開始されているのに『ビデオスタート!』と何度か叫び続け,最後の『ビデオスタート!』で動画が終了する」

というものです。もちろんそのシーンは覚えていて,僕は撮影がやっと始まったよと思い滑り始めたときのものです。多分最後の「ビデオスタート」を「ビデオストップ」と"やっと"誤認識して電子音を鳴らしながら終了したのでしょう。音声認識の精度あんまりよくねえな」とか思ってごめん。結構よく通ります。

ちなみに「ビデオスタート」を「動画スタート」と言い間違えてなかなか指示が通らないことがありました。そりゃそうだ。

それにしても「ビデオスタート」と「ビデオストップ」ってだいぶ混同する音声な気がする。「スタ」と「スト」の[st]って発音一緒だし,最後の「ト」と「プ」も破裂音。どうにかならんだろうか。

 

ちゃんとテストしよう

結局のところ一番の対策はこれです。本番を想定して動作テストをする。使ってみて分からないところに気づく。動画撮影中か分からないな,この電子音はどういう意味だろう。よくわからなくなった時にどうすれば良いだろう。というのをあらかじめ確認しておけばこんな残念なミスはしないで済んだと思います。

 

僕にとっては久しぶりの慣れないスノボが動作テストになったわけですが,色々学びはありました。次はサバゲーあたりで使ってみようと思います。

サピエンス全史を読んだ

kindle を購入してから,会社までの行きかえりと夜寝る前の20分程度が読書の時間になった。ジャンルは大体ノンフィクション。気に入った文言をハイライトしながら読み進めるのが楽しい。

 

激混みの御堂筋線の中,年始からちまちま読み進めてやっと読み終わったのが
サピエンス全史 

ホモサピエンスの台頭から資本主義の隆盛までの歴史的な事象について丁寧に解説した後,歴史を幸福という観点で切り込んで,それからでこれからの人類の幸福について考える本。

なんですが,自分としては下地となる「歴史的な事象について丁寧に解説した」の部分に目からうろこポイントが多すぎて,その辺のことをいくつか振り返ってみたいと思います。

  

認知革命 虚構を語ってこそ人類

この本で出てきた「○○革命」の中で最初に出てきたもの。認知革命。初めて聞いたわ。認知革命によって人類は言語を話し,虚構を語ることができるようになった。嘘をつくという意味ではなく虚構を信仰できるようになったということだ。(嘘なら人類以外の動物もつけるという例を出してくるのがこの本の面白い所) そして虚構によって人類は大勢で協力できるようになった。(虚構なしで協力できるのは150人が上限)

なんだ宗教の話か?と思ったけど以下のものも虚構の信仰である。

急に身近になってきた。

貨幣は確かに「コインや紙に価値がある」という信仰だし,国は人が勝手に決めたチーム分けである。ただし虚構だからといって無意味かというとそうではない。貨幣があるからこそ地球の裏の知らない農家のおじさんが作った食品を食べて,その分知らないおばさんのために新聞を配ったりできる。国があるからこそ異国の地の日本人と会った時に「同じ日本人だもんな」といって仲良くなれる。(こういう経験は僕はないけど「日本人」を「群馬県民」に置き換えればあるにはある) ただしグローバル化の現在,国については本当に虚構なのではという気もしてくる。

人権も虚構というのが意外。虚構といっても否定しているわけではない。かといって事実でもない。そう信じる方がうまくいくから信じているだけだ。だから,例えば人権がこの世の絶対の真理であることを一生懸命説明する必要はない。そういうのを信じる方がなんかいいねで十分だ。なんだか気が楽になってきた。より一層堂々と人権を主張できそう。

 

狩猟採集民は農耕民より豊か

縄文時代は狩りをしたり木の実を採ったりしていた。弥生時代は稲作が始まって畑を耕すようになった。」というのを義務教育の歴史の授業で習った。このころはまだやる気があった。資料集に載ってた当時の食事の違いを見て「縄文時代のほうが肉と魚とフルーツでおいしそう」と思ったのが印象的である。

その小6だか中1だかの僕の印象はすごく正しくて,栄養バランスも労働時間も農耕民より狩猟採集民のほうが幸せな感じだったという。本書の「ホモ・サピエンスが小麦,稲,ジャガイモに"家畜化"された」という表現がめちゃくちゃ面白い。両者の特徴を挙げると…

狩猟採集民

  • 狩りで大物仕留めれば短時間労働でしばらく食える
  • 食べ物の種類が豊富で栄養バランスよい
  • わりと60歳超えいた
  • 将来のこと考えなくて良い

農耕民

  • 作物育てるのに手間がかかるので一日中働いた
  • 移動生活ができないし,できないので他部族と衝突したら即戦争
  • 栄養バランスの偏り
  • 不作=死
  • 数か月後の収穫がうまくいくか心配でストレス
  • 数万年かけて身体が狩り用に進化したから農耕すると腰痛める

農耕民つらそう… 

ちなみに僕は,ゲームCivilization V (Steam:Sid Meier's Civilization® V) という文明を育てて戦うストラテジーゲームが大好きなんですが,テクノロジーツリーの一番最初は「農耕」なんですよ。

「初手から詰んでるじゃないか!」という気持ち。

それではデメリットだらけに見える農耕の何がいいのかというと,(以下,『銃・病原菌・鉄』(著:ジャレド ダイアモンド)の知識も混ざりますが)

  • 単位面積当たりに得られるカロリー量が高い
    →人口密度が増えて大勢で協力でき,軍事力も強くなる
  • 保存がきき,労働カロリーに対して得られるカロリーが多い (1 kcalの労働で50 kcal)
    →食料生産者以外の存在が許される(首長,兵士)

 であり,これが現代まで続く文明発展に欠かせないのです。遺伝子の拡散や文明の発展がそのまま個人の幸福とならないのが悲しいところ…

 

差別はポジティブフィードバックループを持つ

人類は虚構を作ったけどその中には差別構造を持ったヒエラルキーもたくさんありましたよ,という中での話。

黒人を例にとって,

  1. 黒人の奴隷制が二世紀にわたって続いた(初期状態)
  2. そのため白人より黒人は貧しく,教育水準が低かった
  3. 良い報酬の職は白人が占め,黒人は劣っているという発想になる
  4. 黒人を報酬の良い職に採用しなくなる
  5. 2 に戻る

というのが差別の悪循環。多分白人黒人を男性女性やその他格差がある系の属性に置き換えても成り立つと思う。モテる人と非モテとかでもいいと思う。

ポジティブフィードバックループ倒立振子のように不安定なので,最初にちょっとした偶然の出来事でもあればたちまち発散してしまう。そして僕は一応大学で制御工学を学んでいた身として,「不安定な系は制御しなければ!」という本能を持っている。

つまり下図のようなポジティブフィードバックは…

f:id:shinji00:20190131005606p:plain

夜の夜中に何を書いているんだろう…

コントローラ(制御器)を加えてこうするべきだ。

f:id:shinji00:20190131005639p:plain

コントローラを加えてポジティブフィードバックによる発散を抑える

コントローラは被差別者の評判を調整することで豊かさを取り戻させる。

黒人の豊かさを取り戻すためには,黒人はそんなに悪くないぜ!みたいな評判を悪循環ループに突っ込んで適切な値に収束させないといけないのだろう。ただこれは積極的な逆差別になるかもしれない。(企業が女性を優先的に採用する,女性の議員の割合を意図的に増やす 等)

それでも,その評判アップキャンペーンは長い目で見れば一瞬の出来事で済むだろう。その後はちょっとした差別の芽をちまちま摘んでいくことになるのだろう。

制御が上手なら一瞬だけバタバタっと制御出力を出すが,目標値(理想)に収束した後はちょっと理想からずれたものをちょっと逆向きに戻すような動作をする。差別対策を不安定系の制御に例えてみました。

 

貨幣は寛容性の極み

宗教や性別,人種,年齢,性的思考に基づいて差別されることなく世界中のみんなで協力できる」なんて言えば夢物語に聞こえるけど,ある意味では貨幣がこの役割を果たしてきた。キリスト教徒とイスラム教徒も,ローマの人と中国の人も協力できた。いくら性格が悪くてカルト信仰してて差別主義者ななパン職人がいたとしても,「あのー,僕はおいしいワインを作って差し上げるので,どうか作ったパンをいただけないでしょうか…あ,いや,ごめんなさい…」とか言う必要はない。市場でお金渡してパンを受け取って終わり。

昔塾講の生徒に「なぜお金なんてものがあるんですか!お金なんてあっても不幸じゃないですか!」といった,まあ何か嫌なことがあったんだろうなという子がいた。僕はお決まりのように「物々交換じゃ大変でしょ?」という答えをだした。(本来ならお金について何か嫌なことがあったのか聞くべきだったんだろうけど)この説明だとあんまりインパクトないので,目の前のボールペンでも手に取ってこういうこと言えばよかったと思う。「虚構によって大勢で協力できる」の話も踏まえて,

  • このボールペンを使うまでに,ボールペンを製造する人,設計を考える人,材料を作る人,原料掘ってくる人,運ぶ人,どこのお店に運ぶか決める人,売る人,売ってることを知らせる人,いろいろな人が関わっている
  • その人たちはめちゃくちゃ人数が多い,何万人もいるかも
  • その人たちは近くに住んでない,海外の人もいるかも
  • その人たちは君と性格合わないかもしれない,怖い人もいるかも
  • その人たちは君のこと知らないし,信用もしてない
  • けど100均でお金払うだけで,その人たち全員の協力を得ることができて,かつ適切なお返しをしたことになる

なぜお金なんてものがあるのか,と将来子供に聞かれた時の答えはとりあえずこの辺の話をしておこう。ちょっと雑だけど壮大さが伝われば良さそう。

 

時計の話

そんなに本筋の話でないけど面白かった。 

  1. 農業の時代には自然の時間サイクルと共に暮らしていた
    当時は時間の計測をしないしできない,暦も気にしない
  2. 産業革命後,複数人で工場のライン作業に入るため,時間表が生まれた
  3. 工場の時間表に合わせて,公共交通機関,工場付近の飲食店,学校,病院,官庁も時間を気にするようになった
  4. 一国で標準時を定めた(それまで街によって時計がずれているのは当然だった)
  5. ラジオ放送が始まり,何よりも時刻をまず伝えるようになった
  6. 現代では時刻を気にしないことのほうが難しくなるまで時計が浸透した
    歯磨きから通勤まで何をするにも時間をベースに行動している

という話なんだけど,突然出てきた,「ドイツの物理学者がBBCニュースで流れるビッグベンの鐘の音からロンドンの天候を推定する手法を発見してドイツ空軍の支援になった」という余談がツボ。全然本筋関係ないけど数行割いてこのトリビアを教えてくれてありがとうという感じ。

 

 

 

他にも面白ポイントは沢山あったけどキリがないのでここまで。

 

紙の本を読んでいた時には,気になる部分や後で思い出したい部分に付箋を貼っていました。Kindleになってから気軽にハイライトが引けて,その文が後から参照できるのでめちゃくちゃ便利。読み終わった後にハイライトを振り返ると,そういえばこんなところで感動してたなーという気持ちを思い出せて楽しい。

なるほどと思えることが上下巻の隅から隅まで詰まっているので,1冊2000円程度の元は十分すぎるほど取れたのではないかと思います。

 

マイコンプログラムの楽しいとこだけつまみ食い micro:bit 始めました

昨年4月に社会人になってから趣味の電子工作をやらなくなってしまった。平日が忙しくなったり,覚えた遊びも多くなったりして,いつの間にか忘れてしまった。

それでも学生時代なんか作ってる時が一番テンション上がってたなと思ったので,今年こそはあのワクワクを再び!とばかりに,とりあえず帰省ついでに秋葉原散策をした。(僕は現在大阪住みです)

 

秋月,千石と巡って,マルツに入ったときに出会ったのがこの micro:bit

microbit.org

デモ展示が面白くて買っちゃいました。

f:id:shinji00:20190114155612j:plain

ほらマイコンプログラミング始めようぜ

 

めんどくさい作業を全部引っこ抜いたマイコンボード

micro:bit がどんなものかを簡単に言うと,

  • ボタン2つ5×5 LEDマトリクス3軸加速度センサ3軸磁気センサI/O端子たくさんラジオmicro:bit 間専用通信),BLE を搭載
  • ブラウザエディタMicrosoft MakeCode for micro:bit)で開発可能。ブロック(Scratchみたいなドラッグ&ドロップタイプのやつ),JavaScriptPythonが使える。
  • 加速度センサを使った揺れ検知 等の基本的な処理は実装済み。

という感じのマイコンボードです。

要はマイコン工作の面倒なところ(それも楽しみなんだけど…)を引っこ抜いて,いきなりそれっぽいの作れるという感じ。

このお手軽さもそのはず,元々イギリスBBCが教育向けに全小学校に無料配布したものだそう。イギリスの小学生になりたい!

micro:bit は製品そのものだけじゃなくて,そのコミュニティにも魅力があると思うんですが,ここでは省略します。

 

Lチカ? まずは加速度センサで水平器!

加速度センサ好きなので,それの活用からやってみようと思いました。

micro:bit を傾けたら水みたいにLED光るやつ作ってみよ

というわけで早速できたのがこれ。

makecode.microbit.org

(中身見えなかったらリンク先飛んでください)

 

秒でできた。

アカウント登録も,開発環境のダウンロードも,はんだ付けも,xyzのアナログ値をロールとピッチに変換する作業も要らなかった。

明るさ ∝ (x - 2) * roll + (y - 2) * pitch って式だけ何となく思い浮かべて書いた。

それとエディタの左側に出てくるエミュレータが優秀。書き込みにかかるちょっとした時間が無くなる。

 

見た目がかわいいのでペットにした

チュートリアルを見ると,5×5のLED使って

:) ←こんな感じの顔文字が描かれることが多い。

サイズも小さいし,最低限っぽいボタンが2つあってたまごっち感があるので,そうだこれはペットにしようかと思ってさっそく作ってみた。

makecode.microbit.org

(中身見えなかったらリンク先飛んでください)

ペットなので

  • 触ると反応する
  • 餌をあげると食べる

あたりを実装するとよいかなと思った。

www.youtube.com

 

 

 

 

 

 

かわいい。

かわいいので無限に餌(磁石)あげたくなる。

 

micro:bit ちゃん かわいいね~ ほーら餌だよ~」

 

「ホ…シイ…」

f:id:shinji00:20190114172845j:plain

 

「…ん?」

 

「カラダ…ガ… ホシイ…!」

f:id:shinji00:20190114172847j:plain

 

 

「……」

 

 

f:id:shinji00:20190114172851j:plain

micro:bit 用小型バギーカー :MOVE mini


というわけで,micro:bit 用小型バギーカー :MOVE mini を用意しました。

 

次回 micro:bit を触るときはこれを使って,もっとバリバリ動ける感じのにしようと思います。

掃除やデリバリーはできなそうだけど,オドメトリ情報と測距センサつかって何となく部屋の広さ(床の散らかり具合)を測るとかできそう。部屋が狭いと激おこになるとか。

 

教育向けで簡単=忙しい大人向け

あらゆる小難しいことが取り除かれていたので,小学校の「クラス全員に配布」にするくらいならこのくらい簡単なのがいいよなと感心した。先生も大変だし。

一方で,自分も会社から帰って寝る前の30分とかで何かできたので,これは何か作りたい欲とかアイデアがあるけど時間がなくて忙しい大人向けでもあるんじゃないかと思った。

もちろん凝ってくると色々時間かかるんだろうけど,一歩目踏み出せたらまずはそれでいいんじゃないかなと。

 

というわけでまた。

しんじ元年 ブログはじめました

ブログはじめました。

 

始めた理由は2つあります。

まず一つ,今まで思ったことは大抵Twitterに書いていたんですが,どうも140字で何かを語るには窮屈だなと常々思っていたのがあります。

twitter.com

 

話題としては学校の教科教育について思うこととか,最近読んだ人類史の本が面白かったこととか,英語の勉強が続かないとか,ゼルダの伝説が楽しいとか,THETA使ってみたとか…

色々あるんですけどそれらを思いっきり語ろうとしてTwitterに連投するくらいなら,ブログの形でまとめるほうが綺麗に書けるんじゃないかと。後で掘り出す分にも都合がいいんじゃないかと。

年も明けたし,誕生日も迎えたし,ちょうど落合陽一先生が,

140字じゃ足りないし,本一冊は読んでくれない.Note始めます.|落合陽一|note

と言い出したし,Twitter抜け出してブログ始めるにはいい機会だなと思いました。(僕は本書きませんけど)

 

そしてもう一つ,技術的な取り組みの記録を残したいと常々思っていました。

大学時代,電子工作とゲームプログラミングが趣味だったのですが,あまり公に公開することもなく,作品を(中身の仕様やコードも含めて)見せ合って意見交換したりする機会もなく,引きこもりがちな作り方をしてしまったなという後悔があったので。

それとアウトプットする先があると,作るものの質もよくなるんじゃないかと思ったので。

 

そんな理由があってはてなブログを活用してみようと思いました。

よろしくお願いします。