人工知能(画像認識・物体検知)の流れから眼を作ってみました。先日、Jetson Nano のカメラ用に作ったものは、動かすことが出来なかったので、オリジナルなものを設計しなおして、サーボモータでカメラの向きを変えることが出来るようにしました。
眼の設計
今回もFreeCADを使って設計しました。本当は、顔も一部作って、眼の奥の機構は隠したかったのですが、顔の設計は、時間がかかってしまうと思うので、眼とそれを支える土台のみ、最低限のものを設計しました。
土台は、左右を反転させましたが、ほぼ同じものをもう1セット、3Dプリンタで印刷しました。
今回の設計では、眼は、サーボに取り付けたホーンで左右、上下に回転することでカメラの焦点方向を左右上下に動かすことが出来るようになります。
ちなみに、これはバージョン3です。バージョン1、2は、設計してみたものの上手く組み立てることが出来ず、没になりました。実は、今は、3DCADソフトを「Fusion360」に変えてバージョン4を作っています。
眼の制作
制作に使っているものを以下にリストアップします。
六角レンチセット(1.5)、なべ小ねじ黒 M3x10、ドライバー(+2)、電動ドリル、六角軸チタンコーティングドリル刃セット(φ1.5mm、Φ2mm、Φ3mm)、カラービット(段付+0x82)、なべ頭タッピングネジ M2x10、紙ヤスリ(細目#400)は、近所のホームセンターで購入しました。
Amazonで購入したものは、以下にリンクを付けておきます。
3Dプリンターは、別の記事で掲載している。Creality Ender-3 S1 Pro と Sonic Padを使っています。
目に取り付けるカメラは、Raspberry Pi カメラモジュール v3(標準) を使っています。スイッチサイエンスで購入しました。(Amazonでは、取り扱いがほとんどありません。検索して表示されてもかなり高額のものしかありませんでした。)
サーボモータは、半年近く前に購入したMiuzeiのMS18を使いました。
動作電圧 | 4.8V – 6.0V |
周波数 | 50Hz (20ms) |
動作範囲 | 120°±10° (900 – 2100μs) |
中立位置 | 60° (1500μs) |
眼の制作は、動画にまとめました。
両眼が完成しました。
眼を動かしてみる
実際にRaspberry Pi 4B 2台にそれぞれカメラケーブルを装着し、映像を受信できるようにします。今回の記事では、まだ映像解析などは行っていません。
Raspberry Pi の電源は、Ankerのモバイルバッテリーから供給しています。ちなみにケースも中が見えるアクリルケースに新調しました。
サーボモータの制御は、Arduino UNO R3を使用します。
1台のRaspberry Pi 4BとArduino UNO R3を繋いで、制御プログラムを動かすようにします。
サーボモータの制御は、PCA9685 16チャンネル PWMサーボドライバ(I2C接続)を使います。
PCA9685 の16チャンネルのうち、12~15のピンを使います。
サーボモータの電源は、18650リチウムイオン充電池を繋ぎます。これは、過去の記事で作った充電モジュールと昇圧モジュールが一体化したものを使います。
Raspberry Pi 4B の画面は、9インチのタッチモニターを繋ぎます。これにPC切替器を繋いでボタンスイッチでどちらのRaspberry Piと接続するのか切替します。合わせてマウスとキーボードも切り替わります。
すべてを接続したら完成です。
Raspberry Pi でArduino IDEを実行して、以下の試運転プログラム(servo_test_04.ino)を実行して動かしてみました。
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);
#define SERVO_FRQ 50 // サーボモータの周波数
#define SERVO_MSEC 20000 // サーボモータの回転時間(μs)
#define SERVOMIN 900 // 最小パルス幅(μs)
#define SERVOMAX 2100 // 最大パルス幅(μs)
#define SERVOSTOP 1500 // 停止パルス幅(μs)
#define PWMMIN 185 // 最小パルス幅÷回転時間×4096
#define PWMMAX 430 // 最大パルス幅÷回転時間×4096
#define ANGLEMIN 0 // 最小角度(固定)
#define ANGLEMAX 120 // 最大角度
int ServoL1_pin = 15;
int ServoL2_pin = 14;
int ServoR1_pin = 13;
int ServoR2_pin = 12;
int ServoL1_angle;
int ServoL2_angle;
int ServoR1_angle;
int ServoR2_angle;
int delay_ms = 10;
void setup() {
pwm.begin();
pwm.setPWMFreq(SERVO_FRQ);
}
/*
* 右目を動かす
* v : 縦方向の角度(30-90を指定)
* h : 横方向の角度(45-75を指定)
*/
void moveRightEye(int v, int h) {
ServoR1_angle = v;
ServoR2_angle = h;
int vf = map(ServoR1_angle, ANGLEMIN, ANGLEMAX, SERVOMIN, SERVOMAX);
int hf = map(ServoR2_angle, ANGLEMIN, ANGLEMAX, SERVOMIN, SERVOMAX);
pwm.writeMicroseconds(ServoR1_pin, vf);
pwm.writeMicroseconds(ServoR2_pin, hf);
}
void moveLeftEye(int v, int h) {
ServoL1_angle = v;
ServoL2_angle = h;
int vf = map(120 - ServoL1_angle, ANGLEMIN, ANGLEMAX, SERVOMIN, SERVOMAX);
int hf = map(ServoL2_angle, ANGLEMIN, ANGLEMAX, SERVOMIN, SERVOMAX);
pwm.writeMicroseconds(ServoL1_pin, vf);
pwm.writeMicroseconds(ServoL2_pin, hf);
}
void loop() {
// 正面を向く
moveRightEye(60, 60);
moveLeftEye(60, 60);
delay(1000);
// 左右に動かす
for (int i=60; i<=80; i+=2) {
moveRightEye(60, i);
moveLeftEye(60, i);
delay(delay_ms);
}
for (int i=80; i>=40; i-=2) {
moveRightEye(60, i);
moveLeftEye(60, i);
delay(delay_ms);
}
for (int i=40; i<=60; i+=2) {
moveRightEye(60, i);
moveLeftEye(60, i);
delay(delay_ms);
}
delay(1000);
// 寄り目
moveRightEye(60,80);
moveLeftEye(60,40);
delay(1000);
// 離れ目
moveRightEye(60, 40);
moveLeftEye(60, 80);
delay(1000);
// 正面を向く
moveRightEye(60, 60);
moveLeftEye(60, 60);
delay(1000);
// 上下に動かす
for (int i=60; i<=90; i+=3) {
moveRightEye(i, 60);
moveLeftEye(i, 60);
delay(delay_ms);
}
for (int i=90; i>=30; i-=3) {
moveRightEye(i, 60);
moveLeftEye(i, 60);
delay(delay_ms);
}
for (int i=30; i<=60; i+=3) {
moveRightEye(i, 60);
moveLeftEye(i, 60);
delay(delay_ms);
}
delay(1000);
// 上を見る
moveRightEye(30,60);
moveLeftEye(30,60);
delay(1000);
// 下を見る
moveRightEye(90, 60);
moveLeftEye(90, 60);
delay(1000);
// 正面を向く
moveRightEye(60, 60);
moveLeftEye(60, 60);
delay(1000);
// 上下左右に動かす
for (int i=60, j=60; i<=80; i+=2, j+=3) {
moveRightEye(60, i);
moveLeftEye(60, i);
delay(delay_ms);
}
for (int r=0; r<3; r++) {
for (int i=80, j=60; i>=60; i-=2, j+=3) {
moveRightEye(j, i);
moveLeftEye(j, i);
delay(delay_ms);
}
for (int i=60, j=90; i>=40; i-=2, j-=3) {
moveRightEye(j, i);
moveLeftEye(j, i);
delay(delay_ms);
}
for (int i=40, j=60; i<=60; i+=2, j-=3) {
moveRightEye(j, i);
moveLeftEye(j, i);
delay(delay_ms);
}
for (int i=60, j=30; i<=80; i+=2, j+=3) {
moveRightEye(j, i);
moveLeftEye(j, i);
delay(delay_ms);
}
}
for (int i=80, j=60; i>=60; i-=2, j+=3) {
moveRightEye(60, i);
moveLeftEye(60, i);
delay(delay_ms);
}
delay(1000);
exit(0);
}
C動かしたときの動画です。
眼の改良
動作範囲が狭いのと、眼球がむき出しなのが気になります。動作範囲を広くして、顔の設計にも挑戦したいと思います。少し時間がかかってしまいますが、乞うご期待!
以上
コメント