#目次
1. Introduction はじめに
2. Methodology 「筆圧で音がなるボールペン」の作製
- 2.1. 主な使用部品
- 2.2. 構成
- 2.2.1. 圧力センサ
- 2.2.2.BMX055使用9軸センサーモジュール
- 2.2.3. スイッチとスピーカ
- 2.2.4.回路図
- 2.3.プログラミング
- 2.3.1.加速度センサを用いた角度測定
- 2.3.2. 圧力の角度補正
- 2.3.3. 圧力に応じて音を変化させる
- 2.3.4. プログラム全体
3. Results 「筆圧で音がなるボールペン」の動作確認
4. Conclusion まとめ
参考文献
#1. Introduction はじめに
こんにちは!「なつめにわ」と申します。
この記事では、以前作製した「筆圧で音がなるボールペン」について紹介したいと思います。
市販のボールペンに、Arduino Nanoや圧力センサ、加速度センサ、スピーカ等を搭載させました。
「圧力センサ」で筆圧を測定し、その圧力に応じて音が鳴る仕組みです。
「加速度センサ」でボールペンの角度を計算し、圧力に補正をかけています。
このボールペンは、この記事を書く6ヶ月以上前に作製しています。そのため、所々うろ覚えです。
分かりにくい箇所があると思いますが、ご了承ください。
#2. Methodology 「筆圧で音がなるボールペン」の作製
###2.1. 主な使用部品
作製に使用した主な部品について紹介します。※電子部品のリンク先は、秋月電子通商になっています。
商品名 | 使用目的 | 備考 |
---|---|---|
サラサクリップ | 本体 | |
Arduino Nano | 制御用 | |
圧力センサーFSR400 | ボールペンの筆圧を測定するために使用 | |
BMX055使用9軸センサーモジュール | 垂直軸からのボールペンの角度を測定するために使用 | JP6をはんだ付け |
半固定ボリューム 3362P 50kΩ [503] | 圧力センサーの電圧調節用 | |
基板取付用スピーカー 9mm UDX01CLFPH | 音を出すために使用 | |
基板用押ボタンスイッチ(黒・モーメンタリ) PS-70N | ON/OFF用 | |
電池ボックス 単5×1本 リード線 | 電源用 | |
単5サイズ形アルカリ電池 12V ゴールデンパワー製 A23 | 電源用 |
###2.2. 構成
本体には、ゼブラ製のサラサクリップを用いています。外装が透明でボールペン内部を確認しやすく、芯の外径が大きくため圧力センサ全体を押しやすいことが選定理由です。PILOT製のJuiceでも良かったです。
以下の画像は、Arduinoやセンサ等を取り付けた状態のボールペンです。
!
####2.2.1. 圧力センサ
買ったばかりの圧力センサは直径が大きくボールペン内部に入りませんでした。そのため、ボールペン外装の内径以下になるように、センサ外周を少し切っています。
ボールペン外装の一部をヤスリで削り、圧力センサが入るようにしています。芯を出した状態でも、圧力センサが外装に当たらないように、十分な長さで削っています。
カムの底をグルーガンで埋め、山を作ることで筆圧を加えたとき圧力センサ全体が押されるようにしています。
####2.2.2.BMX055使用9軸センサーモジュール
クリップの上端に基板と一緒に、グルーガンで取り付けています。取付向きは下図のようになっています。
####2.2.3. スイッチとスピーカ
キャップ上部にスイッチとスピーカをグルーガンで取り付けています。スピーカ上端を押すことで、スイッチがONになると同時にペン先が出るようになっています。
###2.3.プログラミング
####2.3.1.加速度センサを用いた角度測定
BMX055を用いた加速度の測定は、秋月電子通商のサイトにあるArduinoサンプルプログラム[1]を参考にしています。
以下は、取得した加速度から鉛直方向からのポールペン角度を計算するプログラムです。計算式は、AN-1057加速度センサーによる傾きの検出[2]を参考にしています。
今回の場合、x, y, z軸方向の加速度をそれぞれXaccl, Yaccl, Zacclとすると、鉛直方向からの角度は以下の式で表せます。
θ=\tan^{-1}(\frac{\sqrt{Zaccl^2 + Xaccl^2}}{-Yaccl})
void sensor() {
//BMX055 加速度の読み取り
BMX055_Accl();
xAccl += offxAccl;
yAccl += offyAccl;
zAccl += offzAccl;
theta = atan2(sqrt(zAccl * zAccl + xAccl * xAccl), -yAccl);
}
####2.3.2. 圧力の角度補正
下図のように、ボールペンが垂直であれば、鉛直方向の力がすべてボールペン軸方向に加わります。しかし、ボールペンを傾けると、軸方向に加わる力が小さくなってしまいます。加速度センサから計算した角度を用いて補正し、鉛直方向の力を求めるようにしました。
鉛直方向の力(筆圧)をF、 圧力センサ値:P、 鉛直方向からの角度をθとすると以下のような式で表せます。
F= \frac{圧力センサ値P}{\cos θ}
####2.3.3. 圧力に応じて音を変化させる
人の感覚(知覚)は、刺激強度の対数に比例する[3]ので、圧力の条件を対数で設定しました。
音を出す条件を決めるために、圧力センサーの測定を行いました。
以下のグラフは横軸xが圧力センサの抵抗値x、縦軸がIn(x)です(今回の実験時、圧力センサの初期値は488)。圧力センサの抵抗値は、筆圧を上げると減少します。
普段ボールペンを使用するとき、非常に高い筆圧で書くことは少ないので、普段自分が使用する筆圧の範囲で音階が変化するように、値や半固定抵抗を調整して筆圧の感度を設定することにしました。
In(x)=0付近の変化が大きいことが確認できます。この値付近で音階が変わる条件を複数付けてしまうと、瞬時に音階が変わってしまうため、任意の音を出し続けることが難しいです。なので、今回はIn(x)が2.8以上で音がなるようにしました。
//音の周波数
#define DO 262
#define RE 294
#define MI 330
#define FA 349
#define SO 392
#define RA 440
#define SI 494
#define HDO 523
#define FSR A3 //圧力センサーのピン番号
#define BEATTIME 200 //音を出している時間(msec)
#define SPEAKER 12 //スピーカーの出力ピン番号
int freq[] = {HDO, SI, RA, SO, FA, MI, RE, DO};//音配列
float cpres[8];//圧力条件配列
float pres = 0;//圧力センサ値
float offpres = 0;//圧力センサ初期値
void sound() {
pres = analogRead(FSR);// 圧力センサ値を取得
pres = log(-(pres-offpres))-2.8;//logの変化率が大きい部分を除外するために-2.8
pres = pres / (cos(fai));//角度補正
if(pres<0){//0未満の値を0にする
pres=0;
}
for (int i = 0; i < 8; i++) {//条件が厳し方から判定する
if ( pres >= cpres[i]+0.2) {//ペン先に軽く力を加えた状態から音が鳴るように+0.2
tone(SPEAKER, freq[i], BEATTIME) ; //音を鳴らす
break;//音を鳴らしたらforループを抜ける
}
else {
continue;
}
}
}
####2.3.4. プログラム全体
上記の角度を計算するプログラムと音を出すプログラムを含むプログラム全体を以下に示します。
プログラム全体を表示
#include<Wire.h>
#include<TimerOne.h>
// BMX055 加速度センサのI2Cアドレス
#define Addr_Accl 0x19
// センサーの値を保存するグローバル関数
float xAccl = 0.00;
float yAccl = 0.00;
float zAccl = 0.00;
//角度
float theta = 0.00;
float offxAccl = 0.20;
float offyAccl = -0.50;
float offzAccl = 0.40;
//音の周波数
#define DO 262
#define RE 294
#define MI 330
#define FA 349
#define SO 392
#define RA 440
#define SI 494
#define HDO 523
#define FSR A3 //圧力センサーのピン番号
#define BEATTIME 200 //音を出している時間(msec)
#define SPEAKER 12 //スピーカーの出力ピン番号
//int freq[] = {DO, RE, MI, FA, SO, RA, SI, HDO};//音配列
int freq[] = {HDO, SI, RA, SO, FA, MI, RE, DO};//音配列
float cpres[8];//圧力条件配列
float pres = 0;//圧力センサ値
float offpres = 0;//圧力センサ初期値
void setup() {
// Wire(Arduino-I2C)の初期化
Wire.begin();
Serial.begin(115200); // シリアル通信速度を設定
//BMX055 初期化
BMX055_Init();
delay(1000);
//圧力センサの初期値を取得
for (int i = 0; i < 100; i++) {
offpres += analogRead(FSR);
}
offpres = offpres / 100;
//条件を設定
for (int i = 0; i < 8; i++) {
cpres[7 - i] = 1.8 / 8.0 * (i + 1);
Serial.print(cpres[i]);
Serial.print('\t');
}
}
void loop() {
sensor();
sound();
delay(100);
}
void sound() {
pres = analogRead(FSR);// 圧力センサ値を取得
pres = log(-(pres - offpres)) - 2.8; //logの変化率が大きい部分を除外するために-2.8
pres = pres / (cos(theta));
if (pres <= 0) {
pres = 0;
}
for (int i = 0; i < 8; i++) {//条件が厳し方から判定する
if ( pres >= cpres[i]+0.2) {//ペン先に軽く力を加えた状態から音が鳴るように+0.2
tone(SPEAKER, freq[i], BEATTIME) ; //音を鳴らす
break;//音を鳴らしたらforループを抜ける
}
else {
continue;
}
}
}
void sensor() {
// //BMX055 加速度の読み取り
BMX055_Accl();
xAccl += offxAccl;
yAccl += offyAccl;
zAccl += offzAccl;
theta = atan2(sqrt(zAccl * zAccl + xAccl * xAccl), -yAccl);
}
//=====================================================================================//
void BMX055_Init()
{
//------------------------------------------------------------//
Wire.beginTransmission(Addr_Accl);
Wire.write(0x0F); // Select PMU_Range register
Wire.write(0x03); // Range = +/- 2g
Wire.endTransmission();
delay(100);
//------------------------------------------------------------//
Wire.beginTransmission(Addr_Accl);
Wire.write(0x10); // Select PMU_BW register
Wire.write(0x08); // Bandwidth = 7.81 Hz
Wire.endTransmission();
delay(100);
//------------------------------------------------------------//
Wire.beginTransmission(Addr_Accl);
Wire.write(0x11); // Select PMU_LPW register
Wire.write(0x00); // Normal mode, Sleep duration = 0.5ms
Wire.endTransmission();
delay(100);
}
//=====================================================================================//
void BMX055_Accl()
{
int data[6];
for (int i = 0; i < 6; i++)
{
Wire.beginTransmission(Addr_Accl);
Wire.write((2 + i));// Select data register
Wire.endTransmission();
Wire.requestFrom(Addr_Accl, 1);// Request 1 byte of data
// Read 6 bytes of data
// xAccl lsb, xAccl msb, yAccl lsb, yAccl msb, zAccl lsb, zAccl msb
if (Wire.available() == 1)
data[i] = Wire.read();
}
// Convert the data to 12-bits
xAccl = ((data[1] * 256) + (data[0] & 0xF0)) / 16;
if (xAccl > 2047) xAccl -= 4096;
yAccl = ((data[3] * 256) + (data[2] & 0xF0)) / 16;
if (yAccl > 2047) yAccl -= 4096;
zAccl = ((data[5] * 256) + (data[4] & 0xF0)) / 16;
if (zAccl > 2047) zAccl -= 4096;
xAccl = xAccl * 0.0098; // renge +-2g
yAccl = yAccl * 0.0098; // renge +-2g
zAccl = zAccl * 0.0098; // renge +-2g
}
#3. Results 「筆圧で音がなるボールペン」の動作確認
筆圧で音が出るボールペン! pic.twitter.com/w3imkEoF9T
— なつめにわ (@natsume_garden) March 31, 2020
筆圧によって音が変わることを確認できました。
音階が「ド」「レ」「ミ」~と連続で変化するため、演奏するのは難易度が高いと感じました。任意の音を上手く出すためには、感度調整も必要ですし、練習も必要そうです。
#4. Conclusion まとめ
最後まで読んでいただきありがとうございました!
この記事では、「筆圧で音が鳴るボールペン」の作製について紹介しました。
筆圧の大きさよって音色が変化する機能をボールペンに持たせることができました。
もし改良するなら、演奏がしやすくしたいです!
##参考文献
[1]秋月電子通商のサイトにあるArduinoサンプルプログラム
http://akizukidenshi.com/catalog/g/gK-13010/
[2]AN-1057加速度センサーによる傾きの検出
https://www.analog.com/media/jp/technical-documentation/application-notes/AN-1057_jp.pdf
[3]竹村 和紘ら,“人間の主観的な力知覚モデルの提案とステアリング操作系への応用”,日本機械学会論文集(C編)78 巻 795 号 (2012-11)
https://www.jstage.jst.go.jp/article/kikaic/78/795/78_3705/_pdf