ユニファ開発者ブログ

ユニファ株式会社プロダクトデベロップメント本部メンバーによるブログです。

2019年のプロジェクトをふりかえる 〜そのとき何があったのか、何をしたのか〜

スクラムマスターの渡部です!

はじめに、この記事はUniFa Advent Calendar 2019の記事でもあり、ふりかえり Advent Calendar 2019の記事でもあります。

今回の記事では、12月ということもありますので、この1年で関わったとあるプロジェクトについてふりかえってみたいと思います。

目次
  • はじめに
  • 走り出しフェーズ
  • 予測し難いことこの上ないぞ!?フェーズ
  • 徐々に安定してきた!チームをもっと強くするぞ!フェーズ
  • 一気にベロシティ低下したぞ!フェーズ
  • ラストスパートフェーズ
  • 2020年に向けて一言
  • さいごに

はじめに

ふりかえるにあたって、対象のプロジェクトの全体像、説明で用いるグラフの見かたを軽くご紹介させていただきます。

※ 記載されている数値は全て実際のものです。

バーンダウンチャート

f:id:unifa_tech:20191206163513p:plain

<グラフの見かた>

  • 赤い線:開始日から理想の完了日まで、均等にタスクを消化した場合の残りポイントの推移
  • 青い線:実際に消化されたポイントを差し引いた残りポイントの推移

チームでは相対見積もり(ストーリーポイント)を採用しています。

一見すると、比較的安定した下降線を描いているように見えますが、要注意なのは、トータルの見積もりが500ポイントある(つまり、傾きの変化が僅かに見えても、実際はけっこう差があるぞ)ということです。

そこで、スプリントごとにどれだけのポイントを消化できたかを比較できるグラフも用意していますので、合わせて確認していければと思います。

ベロシティの推移

f:id:unifa_tech:20191206163550p:plain

<グラフの見かた>

  • 赤い線:プロジェクト遂行とは直接は関係ない、調査 / 不具合 / 採用などの作業のポイント(以降、プロジェクト外作業と呼称)*1
  • 青い線:プロジェクト遂行に関わる全ての作業のポイント(以降、プロジェクト内作業と呼称)

おや?先ほどの印象とは打って変わって、かなりファンキーな印象を受けますね。

ではさっそく、フェーズごとにふりかえっていきたいと思います。 (おこったこと一つ一つに深堀りしていくと薄い本が出せる気がするので、今回は深堀りません)

走り出しフェーズ

f:id:unifa_tech:20191206165515p:plainf:id:unifa_tech:20191206165518p:plain

一見、ベロシティは安定して右肩上がりのように見えます。 が、後述しますが、実測値ではないので、このフェーズの数値は強くは意識しないでください。

このフェーズでは(私が関わったのはスプリント4からですが)、何よりもまず優先で、チームの状況を見える化することにフォーカスしました。

やったこと、おこったこと
  • 祝 join!@スプリント4(懐かしい!)
  • 見積もりを取得した(プランニングポーカーの実施)
  • ストーリーポイント見積もりの説明・導入した
  • スプリント毎のベロシティ計測を実施しはじめた
  • バーンダウンチャートを使って、全ての関係者へ状況を見える化した
  • スプリント4以前はベロシティを計測できていなかったので、後日ヒアリングを元に工数を仮置きした(翌スプリント以降のばらつきを考えると、実際計測してたらこんななだらかな線にはならないかも)

予測し難いことこの上ないぞ!?フェーズ

f:id:unifa_tech:20191206165656p:plainf:id:unifa_tech:20191206165659p:plain

バーンダウンチャートでは比較的なだらかに見えますが、ベロシティの推移を見てみると突然の乱高下です。荒ぶっていますね。

このような状況ですと、次のスプリントでどの程度コミットするべきかの予想を立てにくく、不明確なコミットの上に成り立つその先の計画も不明確なものになりかねません。(当時は、昨日の天気*2もブレが大きすぎて参考に出来ない状況でした)

POや関係者とは、「いまの段階で着地の予想は出来なくはないがブレが大きいはずなので、リリース日の決定はまだ待つべきだろう」という話をしていたことを覚えています。

スプリント4まではカイゼンのための土台作りに100%フォーカスしており、上記の状況が明らかになりましたので、スプリント5,6から実際にアクションを実施していきました。

このフェーズでは、透明性の確保にも引き続き勤しみつつも、判明した障害の中でも重要なものから順番に取り除いていました。

やったこと、おこったこと
  • スプリント毎のベロシティが安定しなかった(おや?こいつぁ予測が困難だぞ!?)
  • ありとあらゆるプロジェクト外作業を全てチケット作成し、見積もりを取得しはじめた
  • 結果、プロジェクト外作業が頻繁に発生し、集中できる時間が作れていないことが判明した
  • プロジェクト外作業が発生するプロセスを把握した後、フローを整理して窓口を一本化した(エンジニア直ではなく、一先ず私へくるように)
  • プロジェクト外作業に対して、専任スタッフを設けた(これは非常に効果が高かった)*3
  • 仕様変更/追加が判明したとき、進行中のチケットには含めず、別でチケットを作成することにして、仕様追加/変更によるスケジュールへの影響を可視化した
  • 絶対に必要ではないMTGはキャンセルしてもらい、必要なMTGは時間を朝or夜にずらして、一日の真ん中は可能な限り集中できる時間にした

徐々に安定してきた!チームをもっと強くするぞ!フェーズ

f:id:unifa_tech:20191206165709p:plainf:id:unifa_tech:20191206165712p:plain

チームみんな・関係者全員の協力と、努力の甲斐あって、徐々にベロシティが安定してきました。

ただ、大きな障害は取り除いたものの、まだまだスムーズに作業が進まないことがあります。

最低限の透明性は確保されてきておりましたので、このフェーズでは、日々の作業がもっとスムーズになるように、あれこれと試行錯誤をしていました。

因みに、いまの私のアイデンティティの一部を担っていると言っても過言ではない「ふりかえり」に関心を抱いたのも、このフェーズでのことでした。

やったこと、おこったこと
  • 見積もりが大きいチケットは、だいたい5pt上限で分割するルールにしてみた。結果、チケットごとに何をすべきかが明確になり認識ズレが減り、翌スプリントへの持ち越しも減った
  • 仕様書の記載方法を変更した。具体的には、1画面につきコンフルエンス1ページを用意して仕様は箇条書きで記載し、そのページをみれば、決まっていることの全てが分かる状態にした
  • これまでは実装しながら仕様をFixさせていっていたが、着手するスプリントの前には仕様がFixされている状態にした(前々から準備してはいたが、このタイミングで仕様の整理がようやく追いついた)
  • PO確認待ちで作業が止まったり、複雑な説明をテキスト入力することに時間がかかっていることが判明したので、POを説得してチームの隣に席移動してもらい、いつでも確認相談して良いルールにした(これもかなり助かりました)
  • 仕様に関する質問は、コンフルエンスの該当仕様書の下部に残すルールにした(後から決定の背景を追いやすくて地味に好評。Slackだと流れて後で確認に手間がかかる)
  • アジャイルレトロスペクティブズを参考に、「ふりかえり」の抜本的改革を実施(場の設定 > データの収集 > アイデア探し > アクションの決定 > ふりかえりのふりかえり > ふりかえりの終了 という流れに組み替えた)
  • Twitterで @viva_tweet_x さんを発見、衝撃とともにフォロー & ふりかえり読本を購入
  • ワーキングアグリーメントを定めた。個人的なお気に入りは「作業報告があったら、「Great!!」と行動を称賛する」
  • ふりかえりでBGMを流した。個人的なお気に入りは 楽ジャズ~クラシック / Easy Camel Trio

一気にベロシティ低下したぞ!フェーズ

f:id:unifa_tech:20191206165721p:plainf:id:unifa_tech:20191206165725p:plain

スプリント12からプロジェクト外作業が増え始めてはいましたが、ここで一気にチームのベロシティが低下しました。

が、その後の状況の正確な把握から、PO・関係者含めて検討し、スピーディに対策を打つことができたフェーズでした。

いま思えば、関係者全員の協力による「透明性の確保」、「ふりかえりによるチームの強化・情報の共有の機会」が無ければ、ここまで迅速な対応は出来なかったのではないかなぁと思います。

やったこと、おこったこと
  • QAチームによるテストがスタート
  • テストに関連した質問、バッチの手動実行、データ作成作業などが発生し、ベロシティが急激に低下していたことが判明
  • QAチームとのコミュニケーションを最適化
  • 何でも確認相談できる枠を、朝イチに設けた
  • Bugチケットを簡単に管理するための専用のカンバンをJIRAに用意
  • Bug発見時の確認プロセス、判断のガイドラインを整備し、確認すべきものだけを適切に確認できるようにした
  • 元々の開発チームの作業をよく理解しているエンジニア2名(同じチームではあるが、同時並行で別作業をしてもらっていた)にjoinしてもらう計画をたてた

ラストスパートフェーズ

f:id:unifa_tech:20191206165734p:plainf:id:unifa_tech:20191206165737p:plain

これ以降は、リリースに向けてやる必要があることをとにかくなんでもやっていました。

(テキストに起こすとちょっと恥ずかしい気もしますが…)関係者全員が一致団結しているなぁと強く感じたフェーズでもありました。

やったこと、おこったこと
  • 私、PO、他ビジネスサイド関係者がテストに参加(主に修正確認など)
  • 前フェーズで述べた2名のエンジニアが実際に作業開始
  • POとウィークリー(最後はデイリー)で、Bug優先度チェックを実施。「修正されると嬉しい」レベルのBugは全てPendingした
  • 日々の運用作業も、緊急のものを除き、対応をストップした(関係者の皆様のご理解に感謝)

因みに、上記のような大きな変更を、弊社では「オペレーション One UniFa」と勝手に呼称していました。

そして、開発チーム、QAチーム、PO、デザインチーム、ビジネスメンバー、他関係者全員の協力のお陰で、世の中に価値を届けることができました。

2020年にむけて一言

2019年は、成功失敗、良かったこと悪かったこと問わず、非常にインプットに重きを置いた一年であったように思います。(後半は徐々にアウトプットしはじめましたが)

ですので2020年は、これまで通りインプットしつつも、よりアウトプット(社内外問わず)に重きをおいて活動していく所存です。

ひとまず、社内の「スクラム勉強会」「ふりかえり導入支援」は計画的に実施していきたいと思います。

さいごに

ふりかえってみると、本当に様々なことをやってきたなぁとしみじみ思います。

いま思い返せば「あの時こうしていたら…」という気持ちがよぎらないことも無いですが、そんなときこそノーム・カースの最優先条項を読み上げて次に活かすとし、2019年を気持ちよく終えたいと思います。

私たちが発見したものに関係なく、その時点で知っていたこと、スキルと能力、利用可能なリソース、手元の状況を考えれば、誰もが最高の仕事をしたと理解し、本当に信じています。*4

「いうても自分ら、よく頑張ったさ」と。

それでは引き続き、アドベントカレンダーをお楽しみください!

*1:グラフにどのような数字を含めるべきかについてはこちらの記事で解説していますので、よければご覧ください。tech.unifa-e.com

*2:過去3スプリントのベロシティの平均を、次スプリント時の計画の参考にします。それを「昨日の天気を見る」と言います。

*3:専任スタッフに関しては別記事でも解説しています。tech.unifa-e.com

*4:このテキストは、次の原文をGoogle翻訳したものです。The Prime Directive - Agile Retrospective Resource Wiki

ルクミーフォトリニューアルにおけるデザインワーク

5月に入社したデザイナーのYogaif:id:unifa_tech:20191209181205p:plainです。

この↑アイコンはプライベートの友人が描いてくれました:) 似ているらしい…

今日はルクミーフォトのリニューアルプロジェクトについて、デザイナー視点でざっくり書いてみようと思います。

  • ユニファのデザイナーってどんなことをしているの?
  • 事業会社のインハウスデザイナーってどんなことをしているの?

というようなことに興味のある方はおつきあいください:)

今回の記事で書いていること

  • ルクミーフォトについて
  • リニューアルプロジェクトの進み方とデザイナーの役割
  • 実際の管理画面

ルクミーフォトについて

ルクミーフォトはIoTを使った保育施設向けのフォトサービスです。

プロダクトとしては大きく分けて2つあり、社内ではそれぞれ園管理画面・保護者画面と呼んでいます。

園管理画面
園管理画面:保育園職員が、撮影した写真をアップロードしたり販売設定したりするWebサイト。PCブラウザ

保護者画面
保護者画面:園児の保護者が、園で撮影された写真を閲覧したり購入したりするWebサイト。PCブラウザ・スマートフォンブラウザ

この両方を2018年〜2019年にかけてフルリニューアルしました。

続きを読む

Swift-Jupyter をつかってみました

はじめに

こんにちは、iOSエンジニアのしだです。UniFa Advent Calendarで、普段の開発であまり関わりの少ないメンバーの記事を見てにやにやしてます 😏

こちらは、UniFa Advent Calendar 2019 の12日目の記事になります。

もう2019年が終わりますが、今年は、TensorFlow Dev Sumit 2019があったり、TensorFlow 2.0になったり、O'Reilly主催のTF World '19があったりTensorFlowのアップデートが多かった気がします。 その中で、以下の Swift for TensorFlow (TF World '19) の動画を見ていたところ、何やら Jupyter notebook に Swift を書いていてなんじゃこりゃと思って調べてみたらSwift-Jupyterというものを知ったので使ってみます。

続きを読む

The Pi with Eyes

By Matthew Millar Research Scientist at ユニファ

Purpose:

This blog will cover the progress of an IoT project for computer vision. The final goal will be to locate an individual and to track this individual to see where they are in a room and to tell if they go near a marked area in the room. This will involve; person detection, tracking, and distance measurements from a Raspberry Pi.
Last time we set up the Pi and got things ready to go.
My 2nd Favorite Pi - ユニファ開発者ブログ

This blog post will look at setting up the Pi so it can stream the video feed from a camera. We will cover how to install OpenCV on the Pi and set up a synchronized camera feed from a special Camera Hat which will be discussed later!

IoT:

The IoT is mainly about connecting everything around us to make a holistic data acquisition system. This means all the data that you create is gathered by sensors and sent to the mother node! Let’s break down IoT.
In a nutshell, IoT is a collection of Node or edge devices that gather data by sampling the world around them. This data can range to a large amount because of the different data that can be collected. It can be temperature and weather data, camera feeds, IR sensors, tablets, refrigerators and appliances, and wearable devices to name a few. This is not a comprehensive list as this list is endless and getting longer every day.

Cameras are one of the most common methods for gathering data in an environment. They can be considered the first edge device that was implemented. The world's first security camera came from a chicken farmer who wanted to see who stole his eggs back in 1933. So, the farmer rigged up a camera (like that 35mm film camera professional (use) and hipster have) and set a trap so when the door to the chicken hut was opened the camera would take a picture. He even went so far to put a tin can to make a noise and have the thief look up at the camera [1]. This was the first recorded instance of a security camera. (Yes, the thief was captured and was found guilty).

OpenCV on Pi:

We will need at least one library for using cameras on the Raspberry Pi. That one will be the most common one to use in computer vision projects, OpenCV. This can be complex to install as the older method

pip install opencv-contrib-python

It does not work well with Opencv4 on some Pis. (I tried and failed). So I had to install it from the source. We will go through the steps one by one to get started.
First things first let us get things ready for OpenCV
Update and upgrade the OS first (just like Linux)

sudo apt-get update && sudo apt-get upgrade

Next, we need cmake installed to build the files later.

sudo apt-get install build-essential cmake pkg-config

And then the I/O packages for images

sudo apt-get install libjpeg-dev libtiff5-dev libjasper-dev libpng-dev

And I/O packages for videos

sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev
sudo apt-get install libxvidcore-dev libx264-dev

We need the GTX development libraries for OpenCV so let’s get them too

sudo apt-get install libfontconfig1-dev libcairo2-dev
sudo apt-get install libgdk-pixbuf2.0-dev libpango1.0-dev
sudo apt-get install libgtk2.0-dev libgtk-3-dev

And a library for aiding in Matrix Manipulation

sudo apt-get install libatlas-base-dev gfortran

And finally the HDF5 and QT GUI

sudo apt-get install libhdf5-dev libhdf5-serial-dev libhdf5-103
sudo apt-get install libqtgui4 libqtwebkit4 libqt4-test python3-pyqt5

Now with all that installed and ready to go let's make our environment to use OpenCV in

Step 1 get and install pip (if you don’t have it already)

wget https://bootstrap.pypa.io/get-pip.py
sudo python get-pip.py
sudo python3 get-pip.py
sudo rm -rf ~/.cache/pip

Now the virtual environment manager

sudo pip install virtualenv virtualenvwrapper

We will update the bashrc file now with the needed information

vim ~/.bashrc

and then add this to the bottom of the file (adjust as needed but if you followed the above exactly then there should be no adjustments)

# virtualenv and virtualenvwrapper
export WORKON_HOME=$HOME/.virtualenvs
export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
source /usr/local/bin/virtualenvwrapper.sh

Exit out of the bashrc file and run source to apply all the changes to the bashrc file

source ~/.bashrc

Finally, we can create a new virtual environment

mkvirtualenv myenv_name -p python3

Activate the environment using

Source /path/to/env/myenv_name/bion/activate

And install some needed dependencies.

pip install "picamera[array]"
pip install numpy

Now we can finally get to installing OpenCV from the source

cd ~
wget -O opencv.zip https://github.com/opencv/opencv/archive/4.1.1.zip
wget -O opencv_contrib.zip https://github.com/opencv/opencv_contrib/archive/4.1.1.zip
unzip opencv.zip
unzip opencv_contrib.zip
mv opencv-4.1.1 opencv
mv opencv_contrib-4.1.1 opencv_contrib

This will get all the code you will need to build and install OpenCV
The next step is to make the swap file larger as if you don’t you will not be able to install OpenCV as it takes up too much room

sudo vim /etc/dphys-swapfile

and change the swap size from 100 to 2048. But later on, you will change it back to 100 as using 2048 can burn out your SD card quickly.

# set size to absolute value, leaving empty (default) then uses computed value
#   you most likely don't want this, unless you have an special disk situation
# CONF_SWAPSIZE=100
CONF_SWAPSIZE=2048

After the install, you will change it back to 100 Stop and start the swap service

sudo /etc/init.d/dphys-swapfile stop
sudo /etc/init.d/dphys-swapfile start

Now go back to your virtual environment to work on it. And then we will start to build and configure OpenCV

cd ~/opencv
mkdir build
cd build
cmake -D CMAKE_BUILD_TYPE=RELEASE \
    -D CMAKE_INSTALL_PREFIX=/usr/local \
    -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib/modules \
    -D ENABLE_NEON=ON \
    -D ENABLE_VFPV3=ON \
    -D BUILD_TESTS=OFF \
    -D INSTALL_PYTHON_EXAMPLES=OFF \
    -D OPENCV_ENABLE_NONFREE=ON \
    -D CMAKE_SHARED_LINKER_FLAGS=-latomic \
    -D BUILD_EXAMPLES=OFF ..

What makes this special is that ENABLE_NEON is on so that OpenCV is optimized for the ARM processor.
Now you can run

cmake 

But make sure you are inside the build file use pwd to make sure it is in the directory opencv/build/
Next step after the build is done using the

make -j4

Command to compile the code. The -j4 will make it faster as it will use all 4 cores of the Pi. You can leave off the -j4 to avoid possible race conditions if it freezes during install. This will take a hot minute so get lunch and possibly dinner and come back.
Now you can run

sudo make install
sudo ldconfig

The next step is to change the swap size back in the above steps.

Now we need to make a sym-link from the Opencv to python.

cd /usr/local/lib/python3.7/site-packages/cv2/python-3.7
sudo mv cv2.cpython-37m-arm-linux-gnueabihf.so cv2.so
cd ~/.virtualenvs/cv/lib/python3.7/site-packages/
ln -s /usr/local/lib/python3.7/site-packages/cv2/python-3.7/cv2.so cv2.so

Now if all that worked then you can check to see if it actually worked or not (took me 3 tries so don’t get disheartened).
Activate your virtual environment and run the following commands

Python
>>>Import cv2
>>>Cv2.__version__

The Camera Man:

Now we can turn our attention to building the camera system for the project. Looking at the final goal of the project of distance estimation we will need a synchronized set of cameras. Using two cameras that are calibrated and synchronized can greatly improve the accuracy of distance/depth predictions [2]. The camera kit that I will be using will be the Arducam 5MP Synchronized Stereo Camera Bundle Kit for Raspberry Pi that can be bought from Uctronics [3]. Using this chip will handle all the synchronization issues that come with dual camera feeds.

The Setup:

The setup is pretty straight forward. The first step is to connect the ribbon cable from the ArduCam chip to the camera input on the Pi.
f:id:unifa_tech:20191129113459j:plain
f:id:unifa_tech:20191129113515j:plain
Then place the pins into the right connectors on the HAT this will be the topmost pins on the Pi.
f:id:unifa_tech:20191129113532j:plain
And that's it you are set up and ready to begin installing more packages and dependencies! Let's plug all out stuff in the pi and your setup should look something like this
f:id:unifa_tech:20191129113643j:plain
Now even MORE setup and setting changes!
The first step is to enable the camera. On the Pi Desktop go to

Preferences->Raspberry Pi Configuration->Interfaces.

Then Enable the Camera, SSH, VNC, and I2C by clicking the enable radio button. The next step is to download the SKD for the ArduCam chip from GitHub

git clone https://github.com/ArduCAM/MIPI_Camera.git

Go into the RPI folder in the repo and run this command

chmod +x ./enable_i2c_vc.sh
./enable_i2c_vc.sh

This will enable the i2c_vc.
Guess what we need even more Packages again! So run these commands to install them both

sudo apt-get update && sudo apt-get install libzbar-dev libopencv-dev

This will set you up well so you are finally finished installing packages.
The next step is to make the install the code from the repos so do this

cd MIPI_Camera/RPI
make install

Next, compile the examples for testing

make clean && make

And finally, run it in preview mode (a C program from the chips creators).

./preview_setMode 0

To actually test out your code. If everything worked out ok you should start to see an image being streamed from the device to your Desktop like this
f:id:unifa_tech:20191129113834j:plain
or this
f:id:unifa_tech:20191129113857j:plain

And there you have it you now have an RPi 3B+ with a stereo camera that is synchronized and ready for the next steps.
Next time I will install TensorFlow and start with a simple object detection AI.

Sources:

[1] https://innovativesecurity.com/the-worlds-first-security-camera/

[2] Peleg, S., & Ben-Ezra, M. (n.d.). Stereo panorama with a single camera. Proceedings. 1999 IEEE Computer Society Conference on Computer Vision and Pattern Recognition (Cat. No PR00149). doi: 10.1109/cvpr.1999.786969

[3] https://www.uctronics.com/index.php/arducam-synchronized-stereo-camera-bundle-kit-5mp-for-raspberry-pi-2173.html

ビーコン信号による位置推定(入門)

研究開発部の浅野です。ユニファ2019年アドベントカレンダー10日目の記事は、BLEビーコンを使った位置推定について書きたいと思います。無線信号を活用して位置情報を取得するための方法には様々なアプローチがあります。今回はその中で最もシンプルでわかりやすいビーコン信号強度を利用する方法を紹介します。


構成

ビーコンを活用したサービスは、インドアナビゲーションや近接マーケティングなどのようにビーコン信号を送信(アドバタイズ)する機器が固定されていて移動する人が持つスマホなどでそれを受信する形で応用されていることが多いと思います。しかし、保育園で園児の位置を把握することを考えると、ネットワーク接続が必要になる受信部を壁や遊具に固定して小型の送信装置を園児の名前バッジや衣服などに装着する方が現実的です。

f:id:unifa_tech:20191208112151j:plain:w300:right それを考慮して今回は右のような構成で実験を行います。MacbookからiBeacon信号を送信し、Bluetoothが内蔵されているRaspberry Pi Zero WHを受信装置として3箇所(座標は既知)で信号強度を測定。3つの受信信号強度からそれぞれの距離(d1, d2, d3)を推定してそこからMacBookの位置を割り出します。なお、MacBookからのビーコン信号の送信にはNode.jsのblenoライブラリを使用しました。そして、Raspberry Piでの受信には姉妹ライブラリであるnobleとnobleをバックエンドとして動くnode-beacon-scannerを組み合わせて使いました。


信号強度から距離を推定する

iBeacon受信信号にはTxPowerとRSSI(Received Signal Strength Indication)というフィールドが含まれます。TxPowerはビーコンが発する信号の強さ(実際にはビーコンから1m離れた距離での受信信号強度、単位はdBm)であり、送信側で設定する値です。今回はMacBookから1m離れた地点での実測値(-54.5)を設定しています。一方RSSIは受信した信号の強度(dBm)です。この2つから送信地点と受信地点の距離dを次のように求めることができます(参考)。


d = 10^{\,(TxPower-RSSI)\,/\,(10*n)}

ここで、nはビーコン信号が遮蔽などなく理想的に伝達する空間の場合は2で、通常は環境に応じて最適化する必要があります。今回の実験ではn=2 としています。また、iBeaconはdefaultでは100msごとに信号をアドバタイズしますが、RSSIの値は比較的変動するため各地点とも数秒間の平均をとりました。その結果MacBookと各受信地点との距離(m)は次のように求まりました。


d1 = 1.084, \, d2 = 1.135, \, d3 = 3.055


距離から位置を推定する

各受信地点までの距離が正確に求まる場合は、各地点を中心として半径がそれぞれの距離である円を描くと3つの円が1点で交わります。その点が送信位置です。しかし実際には必ず誤差がのるためそのような幾何的な手法で求めることはできません。そこで、送信位置をxとして、各受信地点との距離の平均二乗誤差を最小にする最適化問題として解いていきます。

import numpy as np

def distance(pos1, pos2): #二点間の距離
    return np.sqrt(sum( (np.array(pos1)-np.array(pos2))**2 ))

def mse(x, locations, distances): #距離の平均二乗誤差
    sum_sqerr = 0.0
    for loc, dist in zip(locations, distances):
        dist_calculated = distance(x, loc)
        sum_sqerr += (dist_calculated - dist)**2
    return sum_sqerr / len(locations)

def midpoint(*args): #中点
    return np.mean(np.array(*args), axis=0)

上のmseが最小にしたい関数です。このように定式化することで受信箇所の数が3より大きい場合にも同様に扱うことができます。最適化問題は例えば下記のようにscipyに含まれるminimize関数を使って解くことができます。

from scipy.optimize import minimize

rec1 = [0, 0]      #受信1の座標
rec2 = [0.1, 1.9]  #受信2の座標
rec3 = [3.65, 2.1] #受信3の座標
d1 = 1.084
d2 = 1.135
d3 = 3.055

locations = [rec1, rec2, rec3]
distances = [d1, d2, d3]
initial_loc = midpoint(locations)

result = minimize(mse, initial_loc, (locations, distances))

f:id:unifa_tech:20191208112229j:plain:w300:right 上のコードで3つの受信地点の座標とそこまでの距離を入力として、距離の平均二乗誤差が最小になる送信地点を求めています。このとき最適化における初期値として3つの受信位置の中点を使っています。この結果求められた送信位置は(0.74, 0.91)でした。正解は(1.2, 1.0)ですのでぴったりではないですがおおよその場所は捉えられています。


まとめ

ビーコンの信号強度を使って送信位置を推定する方法を実装して悪くない結果を得ることができました。今回の実験では送信と受信の間には何も置かず、まわりにもBluetooth機器がそれほどなかったので比較的整った環境でしたが、それでも50cm弱の位置推定誤差になりました。実際の環境では壁、天井、人や物による反射や遮蔽、あるいは他のBluetooth信号との混線などもっとたくさんの誤差要因があります。そういった状況では上述のnの最適化だけでは不十分なことが多く、精度を保つために様々な手法が開発されています。実際のサービスに組み込んでいくためには求められる精度の理解とそれを実現するための必要十分な技術を見極めていくことが重要です。

初めてのre:Invent4日目 (Keynoteとre:Play)

日本に帰ってこんにちは。

時差ボケなのか体力消耗なのか日本に戻って爆睡してしまってた、
ユニファのインフラ見てますすずきです。

今遅ればせながらre:Invent4日目のブログ書いてます。

最終日(re:Invent自体はもう一日ありますが私は最後…)もサクッと認定者ラウンジで朝ごはん食べつつ、
最終日はちゃんと会場でKeynote見ようと会場へ向かいました。

続きを読む

サクッとできるCSSアニメーション(transition)の使い方

Webデザイナーのmorita(こ)です。今回はCSSのアニメーションについて、ちょこっと書かせていただきました。

ボタン(hover me)

↑こんな感じのよくあるhoverアクションを1行で実現します。

 transition: .◯s;

◯の部分は数字でOK。
.4sとかにすると、0.4秒で実行されるアニメーション、
1sとかであれば、1秒で実行されるアニメーションとなります。

※ここ重要!テストに出ます。
【CSS transitionでアニメーションさせるためのポイント】

・対になるプロパティのbefore/afterを用意すること

上記の例だと、

.btn-sample{
  opacity: 1;
  transition: .4s;
}
.btn-sample:hover{
  opacity: 0.3;
}

変化させているプロパティは「opacity(透明度)」なので、beforeは「opacity: 1;(透明度100%)」、afterは「opacity: 0.3;(透明度30%)」です。このあいだの変化を「transition: .4s;(0.4秒)」かけてアニメーションさせています。

例ではCSSでのhoverアクションでしたが、JSなどでの状態変化(clickとかscroll)にもclassを作成して対応できます。(サンプルでは1回しかできません。。)

ボタン(click me)

.move-sample{
  position: relative;
  left: 0;
  transition: .4s;
}
.move-sample.is-anime{
  left: 60%;
}

clickすると、「is-anime」というclassをつけるようにJS書いてもらって、アニメーションのbefore/afterはCSSで制御する感じです。エンジニアとデザイナーが協業で行う時に重宝するやり方です。(JSはトリガーとして、CSSは表現として実装)

transitionの欠点は

・「display: none;」されているとできない。
・ループとできない。

ということです。 上の問題はよくスマホのハンバーガーメニューとかで遭遇しますね。その場合は画面の外に配置したりして、表示する時に画面内に移動してくるとかの対応をしたりしています。 どうしてもこの問題に対応するなら、CSS animationを使うことになります。(その場合、「サクッと」にならないのでググりましょう。)

【他にも色々できちゃったりするよ】
今回はもっとも短く最小のtransitionアニメーションでしたが、他にも様々な設定を指定することが可能です。
transition-property 変化させるプロパティを指定(all,opacity,top/leftなど)
transition-duration 変化させる時間(省略不可、.4sとか秒数を指定)
transition-delay 変化の開始を遅らせる
transition-timing-function イージング(デフォルトはease)

【ブラウザ対応】
IE10以上で実装されているので、ベンダープレフィックスとかもなしでいけるでしょう。

【CSSでアニメーションできる喜び】
少し前まではアニメーションさせるといえば、「JS(jQuery)で」みたいな感じもありましたが、昨今のデザイナーとエンジニアの協業体制を考えると、JSはエンジニア領域、CSSでデザイナー領域として、細部のアニメーションまでを表現(デザイン)できるのが望ましいと感じています。