インターネット上であらゆる種類の自動猫の体操が高価で、機能が制限されていることがわかりました。 繰り返しますが、光線がカーテンに落ちないという保証はなく、破片に裂けないという保証もありません。
または、私たちは電子技術者ではなく、arduino技術者でもありませんか! そして、自分でそれを収集するには?
最初に私は鉄を手に入れました: Arduino Nano 、いくつかの簡単なサーボ(マスターキットのArduinoで購入できます)と、開口部が点在する散らばったポインターからの赤い半導体レーザー。 現時点でLUTomはハンカチを作り、サーボがくっつく場所、レーザー用のトランジスタのキーになり、Arduinピンが過負荷にならないようにしました。
デバイスの概略図:
料金:
通常のUSB-miniは、5 Vの電源からArduino Nanoに接続されていますが、スケッチを埋めるためにコンピューターに接続されています。
もちろん、私はデザインをできるだけシンプルにしたかったのです。 3Dプリンターが役に立ちました。 これは、本当に家の職人の助けです! 私は、ファイルでそれらを見るのではなく、小さな詳細を印刷することが本当に好きでした。 1時間で、 MC5 DROVAは2軸回転装置用に4つの部品を印刷しました。 印刷プロセス自体が非常に魅力的であるため、この時間が気付かれずに飛びました!
塗装済みのベニヤ板に、回転装置と回路基板を組み立てます。 これが経済であり、プログラムします。
ここが楽しい部分です。 猫が興味を失うことのないように、標準外のスペックの動きで猫に興味を持たせ、爪から壁紙とカーテンを保護する必要があります。
実験的には、いくつかの実験を経て、狩猟対象の動きに関する次の原則が選択されました。
-レーザーの攪拌は常に発生するわけではありませんが、斑点をオフにしてランダムに停止し、動物はそれを求めて神経質に見回します。
-動きの軌跡は、セッションごとにランダムに変化します。
-動作範囲も特定の軌道内で変化します。
-軌跡の最後まで移動した後、ポイントはフリーズし、獣はそれをつかむために踏みつけることができます。
-汚れは壁やカーテンに落ちてはならず、床にのみ落ちます!
サーボの動きを交互に行うことで、ポイントとポイントの間の直線と正方形タイプの最も単純な動きにふけると、私はより複雑な軌道を実現したかった。 簡単にグーグルで調べた後、私は古き良きDDAラインアルゴリズムに決め、2点間のラインセグメントをラスタライズしました。 つまり、軌跡関数を設定し、横座標を設定し、縦座標を計算し、2つの座標に沿って小さな連続ステップでレーザーを新しいポイントに移動します。 彼は思いついた道を作りました:ファン、正弦波、扇形、正方形など。 誰かが狩りをしている場合、より複雑な機能を描くことができます。
ネタバレの下で、現在働いているスケッチの全文:
スケッチ
#include <Servo.h> //サーボライブラリ
#include <Metro.h> //タイマーライブラリ
#define laser 7 //このピンでレーザーがオンになります
#define led 13 //組み込みArduino LED
//安全なスポット移動ゾーンを実験的に選択します
int minv = 85; // 10; //スポットの極端に低い位置
int maxv = 115; // 55; //極端なトップ
int minh = 90; // 45; //左端、正面を見る
int maxh = 145; // 120; //右端、正面を見る
符号なしlong DelayBetweenMovements = 1000; //ミリ秒単位の遅延
符号なし長秒= 1000; // 1秒 = 1000ミリ秒
サーボmyservo_ver; //垂直に移動します
サーボmyservo_hor; //水平方向に移動します
ボイド設定()
{
pinMode(レーザー、OUTPUT);
pinMode(led、OUTPUT);
digitalWrite(led、HIGH);
ServoOn();
myservo_hor.write((maxh + minh)/ 2); //レーザーを設定します
myservo_ver.write((maxv + minv)/ 2); //中央の位置
遅延(2000); //数秒間監視する
}
//-メイン----------------------------------------------
ボイドループ()
{
randomSeed(analogRead(0)); //ポート0からのランダムな値でランダムに初期化します
int g =ランダム(1,7); // 7つの中から1つの軌跡をランダムに選択します
randomSeed(analogRead(0));
int tg = random(1,3); // 30、60、または90秒のセッション期間をランダムに選択します
DelayBetweenMovements = second * random(1,5)/ 2; //パスのポイント間の移動時間をランダムに選択します
スイッチ(g){
ケース1:GameRandom(tg * 30 * second); 休憩;
ケース2:GameFan(tg * 30 * second); 休憩;
ケース3:GameFan1(tg * 30 * second); 休憩;
ケース4:GameFan2(tg * 30 * second); 休憩;
ケース5:GameCorners(tg * 30 * second); 休憩;
ケース6:GameSinHor(tg * 30 *秒); 休憩;
ケース7:GameSinVer(tg * 30 *秒); 休憩;
}
遅延(ランダム(10.60)*秒);
}
//-メインを終了-----------------------------------------
//ランダムな振幅の垂直正弦波
void GameSinVer(unsigned long GameTime){
メトロgame_time =メトロ(GameTime);
ServoOn();
servo_move((maxh + minh)/ 2、minv、10);
while(!game_time.check()){
randomSeed(analogRead(0));
int i = minv;
int j =ランダム(1,5);
for(i; i <maxv + 1; i = i + j){
servo_move((maxh + minh)/ 2 +(maxh /(j + 1))* sin(i)、i、10);
遅延(DelayBetweenMovements);
}
// digitalWrite(laser、random(2));
}
ServoOff();
}
//ランダムな振幅の水平正弦波
void GameSinHor(符号なしの長いGameTime){
メトロgame_time =メトロ(GameTime);
ServoOn();
servo_move((maxv + minv)/ 2、minh、10);
while(!game_time.check()){
randomSeed(analogRead(0));
int i = minh;
int j =ランダム(1,5);
for(i; i <maxh + 1; i = i + j){
servo_move(i、(maxv + minv)/ 2 +(maxv /(j + 1))* sin(i)、10);
遅延(DelayBetweenMovements);
}
// digitalWrite(laser、random(2));
}
ServoOff();
}
//ランダムなサイズの正方形
void GameCorners(符号なしの長いGameTime){
メトロgame_time =メトロ(GameTime);
ServoOn();
servo_move((maxh + minh)/ 2、minv、10);
randomSeed(analogRead(0));
int c =ランダム(1,4);
while(!game_time.check()){
int i =ランダム(1,5);
スイッチ(i){
ケース1:
servo_move(minh、(minv + 1)、random(1,4)* 10); 休憩;
ケース2:
servo_move(minh、(maxv + 1)、random(1,4)* 10); 休憩;
ケース3:
servo_move(maxh、(maxv + 1)、random(1,4)* 10); 休憩;
ケース4:
servo_move(maxh、(minv + 1)、random(1,4)* 10); 休憩;
}
遅延(DelayBetweenMovements);
// digitalWrite(laser、random(2));
}
ServoOff();
}
//ランダムスイングのファンの動き
void GameFan(符号なしの長いGameTime){
メトロgame_time =メトロ(GameTime);
ServoOn();
servo_move((maxh + minh)/ 2、minv、10);
while(!game_time.check()){
randomSeed(analogRead(0));
servo_move(ランダム(minh、maxh + 1)、maxv、ランダム(1,4)* 10);
遅延(DelayBetweenMovements);
servo_move((maxh + minh)/ 2、minv、random(1,4)* 10);
遅延(DelayBetweenMovements);
// digitalWrite(laser、random(2));
}
ServoOff();
}
//ファンの動き
void GameFan1(符号なしの長いGameTime){
メトロgame_time =メトロ(GameTime);
ServoOn();
servo_move((maxh + minh)/ 2、maxv、10);
while(!game_time.check()){
randomSeed(analogRead(0));
servo_move(ランダム(minh、maxh + 1)、minv、ランダム(1,4)* 10);
遅延(DelayBetweenMovements);
servo_move((maxh + minh)/ 2、maxv、random(1,4)* 10);
遅延(DelayBetweenMovements);
// digitalWrite(laser、random(2));
}
ServoOff();
}
//ファンの動き
void GameFan2(符号なしの長いGameTime){
メトロgame_time =メトロ(GameTime);
ServoOn();
servo_move((maxh + minh)/ 2、(maxv + minv)/ 2,10);
while(!game_time.check()){
randomSeed(analogRead(0));
servo_move(ランダム(minh、maxh + 1)、ランダム(minv、maxv + 1)、ランダム(1,4)* 10);
遅延(DelayBetweenMovements);
servo_move((maxh + minh)/ 2、(maxv + minv)/ 2、ランダム(1,4)* 10);
遅延(DelayBetweenMovements);
// digitalWrite(laser、random(2));
}
ServoOff();
}
//すべての方向にランダムに移動します
void GameRandom(符号なしの長いGameTime){
メトロgame_time =メトロ(GameTime);
ServoOn();
while(!game_time.check()){
randomSeed(analogRead(0));
servo_move(ランダム(minh、maxh + 1)、ランダム(minv、maxv + 1)、ランダム(1,4)* 10);
遅延(DelayBetweenMovements);
// digitalWrite(laser、random(2));
}
ServoOff();
}
void ServoOn(void){//サーボを有効にしてレーザーをオンにする
myservo_ver.attach(9); //デジタルピン9にサーボを垂直に接続
myservo_hor.attach(8); //デジタルピン8にサーボを水平に取り付けます
digitalWrite(レーザー、1); //レーザーをオンにします
}
void ServoOff(void){//サーボとレーザーを無効にする-休む
myservo_ver.detach();
myservo_hor.detach();
digitalWrite(レーザー、0);
}
//現在のポイントx1、y1からポイントx2、y2に移動し、ステップ間の遅延delay_ms
void Servo_move(ダブルx2、ダブルy2、int delay_ms)
{
double x1 = myservo_hor.read(); // servの現在位置を読み取ります
double y1 = myservo_ver.read();
int iX1 = round(x1); //座標を丸めます
int iY1 = round(y1);
int iX2 = round(x2);
int iY2 = round(y2);
//長さと行の高さ
int deltaX = abs(iX1-iX2);
int deltaY = abs(iY1-iY2);
//必要な最小反復回数をカウントします
//線を引きます。 長さと高さの最大値を選択する
//回線、回線接続を提供
int length = max(deltaX、deltaY);
if(length == 0)return;
//横座標と縦座標に沿って各ステップで増分を計算します
double dX =(x2-x1)/長さ;
double dY =(y2-y1)/長さ;
//初期値
double x = x1;
double y = y1;
//メインループ
長さ++;
while(長さ-)
{
x + = dX;
y + = dY;
myservo_hor.write(x);
myservo_ver.write(y);
遅延(delay_ms);
}
}
#include <Metro.h> //タイマーライブラリ
#define laser 7 //このピンでレーザーがオンになります
#define led 13 //組み込みArduino LED
//安全なスポット移動ゾーンを実験的に選択します
int minv = 85; // 10; //スポットの極端に低い位置
int maxv = 115; // 55; //極端なトップ
int minh = 90; // 45; //左端、正面を見る
int maxh = 145; // 120; //右端、正面を見る
符号なしlong DelayBetweenMovements = 1000; //ミリ秒単位の遅延
符号なし長秒= 1000; // 1秒 = 1000ミリ秒
サーボmyservo_ver; //垂直に移動します
サーボmyservo_hor; //水平方向に移動します
ボイド設定()
{
pinMode(レーザー、OUTPUT);
pinMode(led、OUTPUT);
digitalWrite(led、HIGH);
ServoOn();
myservo_hor.write((maxh + minh)/ 2); //レーザーを設定します
myservo_ver.write((maxv + minv)/ 2); //中央の位置
遅延(2000); //数秒間監視する
}
//-メイン----------------------------------------------
ボイドループ()
{
randomSeed(analogRead(0)); //ポート0からのランダムな値でランダムに初期化します
int g =ランダム(1,7); // 7つの中から1つの軌跡をランダムに選択します
randomSeed(analogRead(0));
int tg = random(1,3); // 30、60、または90秒のセッション期間をランダムに選択します
DelayBetweenMovements = second * random(1,5)/ 2; //パスのポイント間の移動時間をランダムに選択します
スイッチ(g){
ケース1:GameRandom(tg * 30 * second); 休憩;
ケース2:GameFan(tg * 30 * second); 休憩;
ケース3:GameFan1(tg * 30 * second); 休憩;
ケース4:GameFan2(tg * 30 * second); 休憩;
ケース5:GameCorners(tg * 30 * second); 休憩;
ケース6:GameSinHor(tg * 30 *秒); 休憩;
ケース7:GameSinVer(tg * 30 *秒); 休憩;
}
遅延(ランダム(10.60)*秒);
}
//-メインを終了-----------------------------------------
//ランダムな振幅の垂直正弦波
void GameSinVer(unsigned long GameTime){
メトロgame_time =メトロ(GameTime);
ServoOn();
servo_move((maxh + minh)/ 2、minv、10);
while(!game_time.check()){
randomSeed(analogRead(0));
int i = minv;
int j =ランダム(1,5);
for(i; i <maxv + 1; i = i + j){
servo_move((maxh + minh)/ 2 +(maxh /(j + 1))* sin(i)、i、10);
遅延(DelayBetweenMovements);
}
// digitalWrite(laser、random(2));
}
ServoOff();
}
//ランダムな振幅の水平正弦波
void GameSinHor(符号なしの長いGameTime){
メトロgame_time =メトロ(GameTime);
ServoOn();
servo_move((maxv + minv)/ 2、minh、10);
while(!game_time.check()){
randomSeed(analogRead(0));
int i = minh;
int j =ランダム(1,5);
for(i; i <maxh + 1; i = i + j){
servo_move(i、(maxv + minv)/ 2 +(maxv /(j + 1))* sin(i)、10);
遅延(DelayBetweenMovements);
}
// digitalWrite(laser、random(2));
}
ServoOff();
}
//ランダムなサイズの正方形
void GameCorners(符号なしの長いGameTime){
メトロgame_time =メトロ(GameTime);
ServoOn();
servo_move((maxh + minh)/ 2、minv、10);
randomSeed(analogRead(0));
int c =ランダム(1,4);
while(!game_time.check()){
int i =ランダム(1,5);
スイッチ(i){
ケース1:
servo_move(minh、(minv + 1)、random(1,4)* 10); 休憩;
ケース2:
servo_move(minh、(maxv + 1)、random(1,4)* 10); 休憩;
ケース3:
servo_move(maxh、(maxv + 1)、random(1,4)* 10); 休憩;
ケース4:
servo_move(maxh、(minv + 1)、random(1,4)* 10); 休憩;
}
遅延(DelayBetweenMovements);
// digitalWrite(laser、random(2));
}
ServoOff();
}
//ランダムスイングのファンの動き
void GameFan(符号なしの長いGameTime){
メトロgame_time =メトロ(GameTime);
ServoOn();
servo_move((maxh + minh)/ 2、minv、10);
while(!game_time.check()){
randomSeed(analogRead(0));
servo_move(ランダム(minh、maxh + 1)、maxv、ランダム(1,4)* 10);
遅延(DelayBetweenMovements);
servo_move((maxh + minh)/ 2、minv、random(1,4)* 10);
遅延(DelayBetweenMovements);
// digitalWrite(laser、random(2));
}
ServoOff();
}
//ファンの動き
void GameFan1(符号なしの長いGameTime){
メトロgame_time =メトロ(GameTime);
ServoOn();
servo_move((maxh + minh)/ 2、maxv、10);
while(!game_time.check()){
randomSeed(analogRead(0));
servo_move(ランダム(minh、maxh + 1)、minv、ランダム(1,4)* 10);
遅延(DelayBetweenMovements);
servo_move((maxh + minh)/ 2、maxv、random(1,4)* 10);
遅延(DelayBetweenMovements);
// digitalWrite(laser、random(2));
}
ServoOff();
}
//ファンの動き
void GameFan2(符号なしの長いGameTime){
メトロgame_time =メトロ(GameTime);
ServoOn();
servo_move((maxh + minh)/ 2、(maxv + minv)/ 2,10);
while(!game_time.check()){
randomSeed(analogRead(0));
servo_move(ランダム(minh、maxh + 1)、ランダム(minv、maxv + 1)、ランダム(1,4)* 10);
遅延(DelayBetweenMovements);
servo_move((maxh + minh)/ 2、(maxv + minv)/ 2、ランダム(1,4)* 10);
遅延(DelayBetweenMovements);
// digitalWrite(laser、random(2));
}
ServoOff();
}
//すべての方向にランダムに移動します
void GameRandom(符号なしの長いGameTime){
メトロgame_time =メトロ(GameTime);
ServoOn();
while(!game_time.check()){
randomSeed(analogRead(0));
servo_move(ランダム(minh、maxh + 1)、ランダム(minv、maxv + 1)、ランダム(1,4)* 10);
遅延(DelayBetweenMovements);
// digitalWrite(laser、random(2));
}
ServoOff();
}
void ServoOn(void){//サーボを有効にしてレーザーをオンにする
myservo_ver.attach(9); //デジタルピン9にサーボを垂直に接続
myservo_hor.attach(8); //デジタルピン8にサーボを水平に取り付けます
digitalWrite(レーザー、1); //レーザーをオンにします
}
void ServoOff(void){//サーボとレーザーを無効にする-休む
myservo_ver.detach();
myservo_hor.detach();
digitalWrite(レーザー、0);
}
//現在のポイントx1、y1からポイントx2、y2に移動し、ステップ間の遅延delay_ms
void Servo_move(ダブルx2、ダブルy2、int delay_ms)
{
double x1 = myservo_hor.read(); // servの現在位置を読み取ります
double y1 = myservo_ver.read();
int iX1 = round(x1); //座標を丸めます
int iY1 = round(y1);
int iX2 = round(x2);
int iY2 = round(y2);
//長さと行の高さ
int deltaX = abs(iX1-iX2);
int deltaY = abs(iY1-iY2);
//必要な最小反復回数をカウントします
//線を引きます。 長さと高さの最大値を選択する
//回線、回線接続を提供
int length = max(deltaX、deltaY);
if(length == 0)return;
//横座標と縦座標に沿って各ステップで増分を計算します
double dX =(x2-x1)/長さ;
double dY =(y2-y1)/長さ;
//初期値
double x = x1;
double y = y1;
//メインループ
長さ++;
while(長さ-)
{
x + = dX;
y + = dY;
myservo_hor.write(x);
myservo_ver.write(y);
遅延(delay_ms);
}
}
練習。
最初のレーザーは1週間後に故障しました-結論は壊れましたが、それらはより線から作られました。 2番目は、調査結果を螺旋状に形成しました。 助けた。 接着剤銃からリードが体から出てくる場所にまだ接着剤を滴下することができます。
すべての電源アダプターが適しているわけではありません。 Arduinoは一部から起動しないか、レーザーがひきつります。 どうやら、出力での大きなリップル。 コンデンサははんだ付けするのが面倒で、良いアダプターを手に入れただけでした。
数匹の猫でテスト済み。 若者は長い間身に着けていた。 古い-彼らは走り回って、横になって汚れがどのように覆い隠されているのか、それがぶつかり、動くので機械自体を見つめています。 Pawはマシンを試しませんでした。 しかし、念のため、彼は紙箱からケーシングを思いついた。
実験中、壁紙、カーテン、猫は影響を受けませんでした。
一般的に、私はすべての電子猫仲間にこの種のことをお勧めします!