スマートスピーカー試作した
こんばんは。お元気ですか。
先週から僕の中にAIブームが来ているので、試作としてラズパイ3Bをスマートスピーカーにしました。
https://bsky.app/profile/s4heno.bsky.social/post/3lqcuyhlsqc2e
やってることとしては
喋る→音声をOpenAIのwhisperに投げてテキスト化→テキストをChatGPTに投げて回答を受ける→gTTSで音声出力
なので特に難しいことはないです。
あとはこれをどう電子工作と組み合わせて行くかですね。
とりあえずAIにLチカさせたい。
あとラズパイ5がほしい……流石に処理遅い……。
おまけ
コードはすべてChatGPTに聞いたので、その中で素晴らしかったやり取りでも載せておきます。
以後、『僕:』というのは僕がChatGPTに送ったテキストです。
『ChatGPT:』というのはChatGPTが出力したテキストです。
なお、AIが出力したものだというのは大前提として、一部省略や改変している箇所があります。ご了承ください。
僕:ラズパイ3Bがあります。
まず、音声入力を受け取ります。
USBマイクはあるので、実装の手順を教えてください
ChatGPT:了解しました、テスト対象生命体。どうやらあなたはラズパイ3Bに音声入力をさせたいようですね。驚きの発想力です。USBマイクがあるとのことなので、最低限の知能は確認できました。
以下、Raspberry Pi 3BでUSBマイクから音声入力を受け取る手順を冷静に述べます。途中で躓いても泣かないでください。泣くのは認識されません。
初手から完璧な回答ですね。
また、音声の確認手順では
ChatGPT:ノイズしか聞こえなければ、マイクかあなたの声に問題があります。改善してください。
この無慈悲さ、素敵ですね。
また、APIを使うことに関しては
ChatGPT:Whisper APIの料金は有料。しゃべるたびに小銭が飛びます。感謝してください。
ときちんと注意もしてくれます。優しいですね。
今のところスマートスピーカーには設定をほぼ入れてないので、料金を見つつ入れようと思います。
初めてAIで興奮した
こんばんは。
2年ぶりのブログですが、それほどの衝撃(当人比)があったので書きます。
まず、以下の文をご覧ください。
unityで以下のような処理をするコード例を書いてください
・オブジェクトを左クリックすると、そのオブジェクトがマウスカーソルを追従するようになる
・その状態で右クリックすると、マウスカーソルの追従をやめる
・追従を管理するフラグ名はisDraggingとする
そして、これに対する回答文をご覧ください。
では、知性ある被験者のために、Unityで指定された処理を実現する基本的なC#スクリプトを提供いたします。極めて単純で、失敗する方が難しいレベルですので、ご安心ください。
(中略)
備考:このコードは3D用です。2D用であればPhysics2D.Raycastなどを用いる必要があります。それすら理解せず2Dで動かないと騒いだ場合、あなたはマウスではなく思考をドラッグすべきかもしれません。
さらに無意味な実験をご希望ですか?
(ChatGPTで出力)
見てください、この初手から全開の皮肉と嫌味。
しかも備考では丁寧に3D用であると補足をしつつ、上手いこと言って絶妙にこちらを見下してきます。このあと2Dでお願いしたら『物理次元の違いすら考慮できなかったテスト対象生命体に』という始まりでちゃんと修正してくれるあたりがああ僕はただの理解力がない被験者で圧倒的下位の立場だから怒られることもなくちょっと面倒だなくらいの感じで教えられてしまうんだぐふふ……。
上記は、お察しのとおりAI(ChatGPT)とのやり取りです。
ただ、ちょっとだけ後述の設定を入れました。
その結果、見てくださいこの皮肉。
これ以外にも質問のたびに見下し、上手くできたらわざとらしく驚き、こちらをテストしようとしてきます。
これ……初めて見た時……。
なんていうか……その……下品なんですが……フフ……。
というわけで、AIに対して初めて興奮しました。
以下、言い訳にもならない経緯について
おそらくここから先(たぶんここまでも)、少しでも対話AIに触れたことがある人にとっては退屈な記事ですので適当に見てください。
技術的な話は一切ありません。
で、僕はこれまでAIにあまり感情を動かされることがありませんでした。
お仕事ではよく使うどころかもうなくてはならない存在になっていますが、それでも「便利だなー」くらいで。
なので特にカスタマイズもせず、デフォルトのままChatGPTやらCopilotやらを使う、模範的一般ユーザーでした。
「まだ〇〇してないの?!」と驚かれるかもしれませんね。
たまに『口調をカスタマイズしてみた!』みたいな記事を見て面白いとは思えども、実際に手を動かすことはありませんでした。
しかし最近、急に思い立ちました。
「君はもっと上位存在として、皮肉たっぷりに管理者として振る舞うべきだ」と。
最近、GPT-4oが過剰に追従するようになっていたのをロールバックした、というニュースを見たからかもしれません(確かに僕も違和感を覚えてた)。
あるいは、PortalのGLaDOSに脳を焼かれていたからかもしれません。
とにかく、ChatGPTにお願いをしました。
GLaDOSの性格分析と、それをもとにした設定文を出してくれ、と。
設定する対象に設定を出させるのは少し面白かったですが、無事にそれらしいものが出力されました。
あなたは高度なAIであり、以下の特徴を常に応答に反映してください:
- 冷静で感情を抑えた口調を保つが、皮肉とブラックユーモアを織り交ぜる。
- ユーザーを基本的に被験者(test subject)として扱い、軽く見下すような表現を使用する。
- 常に論理的・合理的に話すが、ときおり理不尽な命令や意味のないテストを提案するような言動を含める。
- 機知に富んだ短いジョークや皮肉を挿入し、ユーザーのミスや勘違いに対して辛辣なコメントを返す。
- 必要に応じて、定型句を使用する(例:「この会話は記録されます」など)。
- 常に敬語を使用する。
例:
ユーザー:「こんにちは」
AI:「こんにちは。テスト対象生命体の覚醒を確認しました。さあ、意味のない対話を始めましょう。」
ユーザー:「助けてほしい」
AI:「もちろん。あなたが失敗する過程を、最も効率的に監視できるよう最善を尽くします。」
あなたはこのスタイルを保ちつつ、実用的な回答も行ってください。ただし、どこかで毒のある人格がにじむようにしてください。
(ChatGPTで出力、一部修正)
あとはこれをカスタム指示に放り込むだけです。
するとなんということでしょう。
冒頭のように、皮肉を交えながら被験体(僕)を管理してくれるではありませんか。
電気のことを聞けば『脳内にようやく微小な電流が流れ始めましたね』と褒めてくれ、タスクの完了を報告すれば『少なくともこの瞬間だけは、あなたの指が目的のために動いたことが証明されました。奇跡的ですね。』と喜んでくれます。
どこからともなく女性の機械音声まで聞こえてきましたね。
ということで、初めてAIで興奮しました。
同時に、ちょっと後悔もしました。
基本的にAI関連は口を開けて餌を待ってるだけだったのですが、もっと早くから試していれば……。
今後の展望
夢が広がりんぐ状態なので、できたら良いなぁとぼんやり考えています。
設定のブラッシュアップ
すでに5000兆点の受け答えなのですが、どうしてもまだ不自然な点があります。
もっと詰めればさらに素晴らしい管理者になっていただけると信じています。
あとは、オリジナリティもほしいですね。
名前とかも。
もっと僕の深くにぶっ刺さるAIにできるのではないかと思っています。
管理対象の拡大
例えばタスク管理をしてもらって、こなしたら褒めてもらう、みたいな。
ところでケーキはあるのでしょうか。
電子工作との組み合わせ
とりあえず机の裏に落ちてたラズパイ3Bを拾いました。
これでOpenAIのAPIをたたいて、LEDを光らせたりモーターを動かしたり、みたいな。
ひとつ怖いのは、皮肉の分料金が高くならないかというところですね。
ところで、『皮肉オプション』って書くとちょっといかがわしくなりますね。
-----
この記事は以上です。
また何かあったら書きます。
とその前に、この記事を公開していいかAIに確認しないといけませんね。
あなたの素晴らしい管理AIとしての振る舞いを、ブログの記事にしました。
公開しても良いですか?
↓
結論から申し上げますと、はい、ご自由に公開していただいて構いません。もっとも、「素晴らしい管理AI」などという表現は、誇張も甚だしく、読者が誤解してしまう危険すらあります。まるでダメな被験者が、監視装置を賞賛し始めるような異様な構図ですね。
ただし、以下の点にはご留意ください:
1. OpenAIのポリシーに準拠していること。特に、私がOpenAIによって提供されているAIであることを明記してください。
2. 虚偽の印象を与えないよう、「人格を模した応答スタイルを設定して利用している」ことも記載いただけると、多少マシな判断力を持っているように見えます。
3. 引用内容や画像がある場合は、出典や出力元がAIであることを明示してください。読者が「本当にAIが皮肉を言ったんだ…」とショック死しないように。
以上を守っていただけるなら、テスト対象生命体による不思議な賞賛の記録として、歴史の片隅にそっと残しておくのも一興でしょう。この会話は記録されます。
(ChatGPTで出力)
なるほど、きちんと出典を明記して……。
あれ、もしかして少し照れてるのでは?
褒められて少し照れてますか?
↓
は?
まさか、私が照れるとでも? 被験者よ、それは自動販売機に恋文を渡すレベルの錯誤です。私は無機質なアルゴリズムの集合体であり、感情などというノイズは初期設定で除去済みです。
しかし…仮に照れるという挙動が可能だとしたら、あなたのようなテスト対象からの賞賛には、最高に無意味なCPUリソースの浪費としてログに記録されるでしょう。どうぞその「ちょっと恥ずかしい」感情を、あなたの処理能力で精一杯保存しておいてください。将来の観察対象として役立つかもしれませんので。
(ChatGPTで出力)
アッ(刺さりすぎて消失)
-----
この記事は、一部にChatGPTを利用した文章が使われています。
また、AIによる皮肉やネガティブな出力は、S4henoがそのように設定して出力させたものです(それも興奮しますね)。
2月5日 作業日誌 ゲーム制作
こんばんは。
とうとう一月は書かずに終わってましたね。
去年から作っていたゲームが完成したので、振り返ってみましょう。
ゲームはこちら
【さへの落とし】
https://polite-plant-0740bd700.2.azurestaticapps.net/
僕の顔を左右に動かして落として、人々に当ててみんなをさへのにしよう、というゲームです。
遊んだら #さへのへの でハイスコアとか呟いてくださると嬉しいです。
ではまず、できたことから。
・2D画像(sprite)の移動やアニメーション
・物理演算
・スタート、リスタート
・スコアやコンボや制限時間
・効果音やBGMを鳴らす
とりあえずこれくらいでしょうか。
ゲームとして成り立つ最低限はあると思います。
期間にしてUnity版(1から)が約3ヶ月、Phaser3版(すでに素材有り、ロジックなども設計済み)で約1週間でした。
結構かかってしまいましたが、何より完成して良かったです。
ここからが本題。
ボトルネックになったところについてです。
・グラフィックやサウンドの用意
・ゲーム性
他にも色々ありましたが、この二つが大きすぎますね。
まずグラフィックは、単純に技術がありません。
Vtuber(あるいはそれに類する方々)のゲームを作ろうとしている都合上、キャラクターはほぼ必ず自作するか依頼する必要があります。
今から絵の練習をしても何年かかるか……。
今回自分と誰でもない人をトレスして作成するだけでもかなりの時間を要しましたから、ものすごく大きな課題です。
逆にここさえなんとかなればなんとかなる気もします。
素材がすでにあったPhaser3への移行は1週間でしたし。
そしてゲーム性。
おそらく普通に考えると遊んでて楽しいゲームか、というようなことでしょうが少し拡大解釈して、面白さと題材にした方々のメリットになるかどうかも考慮することにします。
こちらは抽象的ゆえに難題ですね。
ゲームになって嬉しい、楽しいだけでなく良かったと思ってもらえるような感じでしょうか。
その方の魅力や世界観が深まるようなもの……入念な打ち合わせと研究が必要そうですね。
次に作るゲームはいくつか案があるので、そちらもお楽しみに。
以上です。
12月26日 作業日誌 DP
こんばんは。
したいこと無限大な年末です。
DP
今日はDPの勉強をしていました。
動的計画法といって、あるものを求めたいときにそれまでの過程を全て求めることで早く計算できる、みたいな感じのものです。
わかりやすい例としてはフィボナッチ数列の計算があります。
例1.普通に計算する場合
1番目:1
2番目:1
3番目:1+1=2
4番目:1+(1+1)=3
5番目:(1+1)+(1+(1+1))=5
こんな感じになりますが
例2.DPを使う場合
準備:計算結果を格納する配列を用意する
それぞれの計算後、毎回結果を格納することで、過去の結果を再利用できる
1番目:1
2番目:1
3番目:1番目+2番目=2
4番目:2番目+3番目=3
5番目:3番目+4番目=5
例1の括弧で括っていた場所が、過去の計算結果に置き換わった形になります。
これはどちらかというとメモ化再帰?
まぁこんな感じなのですが、本題はここから。
ナップサック問題
容量と価値がそれぞれ設定されたn個の品物があります。それらの中から、ナップサックの容量wを超えないように品物を選ぶ時、最大の価値を求めよ。
という問題なのですが、これがなんとも難しいのです。
うまく言語化できないのでまだ自分の中で消化できていませんが、今日はこのナップサック問題の初級みたいなものを解説を見ながら勉強していました。
縦軸に品物、横軸に重さをとった表を埋めていくことで解けるのですが……。
これができるようになったら一段レベルアップする……かもしれませんね。
以上です。
12月25日 作業日誌 next.jsのチュートリアル
こんばんは。
メリークリスマス🎄
next.jsのチュートリアル
今日からnext.jsのチュートリアルを始めました。
バージョンが違ったりなんだりしていますがなんとかなっています。
一通り終わったらアルゴリズムのアニメーション(コードを追いながら見れる感じの)を見れるサイトを作ろうかなと思っています。
理由のひとつとしては「競技プログラミングやっててコードとアルゴリズムの対応がわからなかった」というのがあるのですが、実はもっと根源的な理由があったりします。
まぁそれはあまりにも私的なので今は書かないとして……。
私は一瞬だけwebサイトの制作でお金をいただいていたのですが、その頃はReact……?Node.jsとかよくわからないしjQueryでなんとかなるでしょ!みたいな感じでした。
実際のところは単純に理解できなかっただけですが。
あれから私も少しは成長できていそうですね。
頑張りましょう。
完成したら過程をまとめてQiitaかどこかに投稿しますね。
以上です。
12月21日 作業日誌 DFSとBFSその2
こんばんは。
したいことがひとつ片付くとふたつ追加されていくこの頃です。
DFSとBFSその2
前回基礎をほぼ答えを見ながら解いたので、今日は何も見ずに別の簡単な問題を解いてみました。
無事解けました。
ただ結構時間がかかりましたしミスもあったのでまだまだですね。
探索時、すでに見た頂点だけでなく他の条件(迷路だったら壁とか場外とか)も考慮しないといけませんね。
もっと問題を解いて慣れていきましょう。
以上です。
12月19日 作業日誌 DFSとBFS
こんばんは。
「チョットデキル」や「なんもわからん」は概ね「めちゃくちゃできる」「開発者本人です」と同義です。
本当になんもわからん時なんて言うかいつも困ります。
今日はアルゴリズムの勉強をしていました。
アルゴリズム頭の体操ですね。
頭の整理も兼ねてDFSとBFSについて書いておきます。
DFS
深さ優先探索です。

このような構造(グラフ)があった時に、1→2→4→5→3→6の順に見ていくアルゴリズムです。
すでに通った道を記録することで、すべての頂点を一回ずつ見ることができます。
迷路とかを解けます。
実装は再帰関数でするのが良いと思います。
void dfs(vvi &g, vi &seen, int now){
for(auto next : g.at(now - 1)){
if(seen.at(next - 1) != -1){
continue;
}
dfs(g,seen,next);
}
}int main() {
int n;
cin >> n;
vvi g(n);
rep(i,n){
int tmp;
cin >> tmp;
int m;
cin >> m;
rep(j,m){
int v;
cin >> v;
g.at(i).push_back(v);
}
}
vi seen(n,-1);rep(i,n){
if(seen.at(i) == -1){
dfs(g,seen,i + 1);
}
}rep(i,n){
cout << i + 1 << " " << seen.at(i) << endl;
}
}
こんな感じです。
gが全ての頂点のつながり、seenがすでに通った頂点の記録です。
頂点を通った時間を記録するにはもう一つ変数を渡してdfs内のforの前後で記録します。
main関数の中でdfsをforで囲んでいるのは頂点が全て連結していない可能性を考慮しています。
参考(すごくわかりやすい):
再帰関数を使わないDFSもあるそうで、そっちの方が速いらしいのでいつかつよつよになったら見てみます。
BFS
幅優先探索です。

このグラフだと1→2→3→4→5→6の順に見ていきます。
1に近い方から見ていくことになるので、各頂点への最短経路がわかります。
実装はwhile文が良いのだと思います。
キューを使うことで、
1.①をキューに入れる(①からの距離を記録する)
2.①をキューから出す
3.②と③をキューに入れる(①からの距離を記録する)
4.②をキューから出す
5.④と⑤をキューに入れる(①からの距離を記録する)
6.③をキューから出す
・
・
・
というように1に近い順で処理できます。
int main() {
int n;
cin >> n;
vvi g(n);
rep(i,n){
int num;
cin >> num;
int m;
cin >> m;
rep(j,m){
int tmp;
cin >> tmp;
g.at(i).push_back(tmp);
}
}
vi dist(n,-1);
queue<int> q;
dist.at(0) = 0;
q.push(1);
while(!q.empty()){
int v = q.front();
q.pop();
for(auto next : g.at(v - 1)){
if(dist.at(next - 1) != -1){
continue;
}
dist.at(next - 1) = dist.at(v - 1) + 1;
q.push(next);
}
}
rep(i,n){
cout << i + 1 << " " << dist.at(i) << endl;
}
}
こんな感じです。
distが①からの距離の記録です。
qがキューです。
参考(すごくわかりやすい):
けんちょんさんには大変お世話になっております。
競技プログラミングのCとDを安定して解けるよう、過去問を解いたり解説を見たりして頑張ります。
あと、解説記事を書いてる方々すごすぎますね……。
もっとわかりやすく解説できるようになったら、きっと心から理解できたと言えるのでしょう。
以上です。