工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

帆船の自動操縦(自動操舵システム)

コンポーネントと消耗品

>
Arduino UNO
× 1
Arduino Nano R3
× 1

このプロジェクトについて

序文:

私は一人でセーリングをするのが大好きです。なぜなら、男性が帆船を持って海にいるとき、彼はより高いレベルに進化するために必要なすべてのものを手に入れたからです。悪天候の生の海でのセーリングは非常に難しいかもしれませんが、彼が太陽と風の良い天気の良い日を選ぶなら、楽しむことは最大になります。

幸福とは、無限の視野、完璧な運動技術、最適な選択だけでなく、おいしい飲み物やおいしいサンドイッチとしての人間的なものも意味します。まさにこの時期にオートパイロットが役立ちます。海で午後5時のお茶とビスケットを飲んでいる間は、代わりに機能します。 :-)

オートパイロットがあなたのためにできること:

帆船にはエンジンがなく、港からビーチ、そして釣り場まで、プログラムされた経路をたどることができず、灯台を回って戻ることはできません。

すべての作業はセーラーによって行われます。この時点でそれを理解する必要があります。帆のトリミング、天候と風の源/速度の制御、ロープの硬化または解放、他のボートとの交通の気遣い、方向と操舵の決定...いつセーラーは休憩をとることに決めました。たとえば、10秒以上(有名な「ティータイム」)にすると、オートパイロットがオンになります。数秒で、そのGPSはボートの位置、速度、方向を取得し、方向(ルート)を維持することができます。ステアリングシステムは、通常は熟練した船員の手によって動かされる、舵に接続されたスティックであり、滑車とロープによって接続されたステッピングモーターを介してオートパイロットによって制御されています。

舵を制御する 微調整または大まかな調整の継続的な作業です。ボートは小さい(軽い)ほど大きくなり、それに影響を与える方向要因の変化が大きくなります。海の波、風の方向と圧力、船員の動きによる船内の重量の移動、海流です。ただし、自動操縦がオンになっている場合でも、セーラーは常に目を覚ましており、リモートコントロールによって実際のルートを変更します。 :その上に+ 1 -1 +10 -10というラベルの付いた4つのボタンがあり、度の小さなまたは大きな変化のために、値を増減します。これらのボタンは自動操縦にあります 緑(右)と赤(左)も。青いボタン(中央)は、自動操縦、一時停止をアクティブまたは非アクティブにするためのものです。 パラメータの設定用の黒いボタンでもあります メモリ内。

<図>

回路:

主な処理はMCU Arduino Uno によって行われます 。もう1つのMCU、 Arduino Nano 、ウォッチドッグです:Uno内に一種のウォッチドッグが存在することは知っていますが、独立した外部マイクロコントローラーを使用してそれを行うのが好きでした。これは私がカバーした生涯の夢です。今は幸せです。 Unoは、ピン3-> A0を介してNanoにフィードし、2.5秒ごとに少なくとも1回(feedingInterval)、ハイ/ロー、5/0ボルトにする必要があります。そうでない場合は、宇野が「眠っている」または「ブロックされている」ことを意味し、Nanoは宇野をリセットします...それはまだ起こっていません、信じられますか?

人気のあるディスプレイを使用しています i2c回路コンバーターと組み合わせて 両方をはんだ付けし、最終的に4本のワイヤーを使用して、Unoと通信するためのデジタルピンを大幅に節約しました。また、ボタンとリモコンを接続する方法 可能な限り少ないMCUのポートを使用するという目標を達成するために、抵抗分圧器によって行われます。 1%の精度の抵抗を選択しました。アナログ比較値は、コードに入力した値の間にある必要があります。他の種類の抵抗を選択したために一部のボタンが認識されない場合は、定数にも変更を加えてください(「checkRfRC()」および「checkHWButtons()」でコードを変更してください)。 RF 433Mhzリモートコントロール(RC)回路はうまく機能します。距離カバレッジと成功の可能性を向上させるために、銅線で自分で作成できるコイルアンテナを追加しました。 10メートル離れた場所でテストしましたが、オートパイロットのテストに使用したターゲットの帆船の長さがわずか4.20メートルであることを考えると、20メートル以上でも十分に機能すると思います。

GPSユニットの場合 最初は良いEM406Aを使用していましたが、残念ながら、Week-Rollover-Bugに苦しんでいることに気付きました。古すぎたため、優れた人気のあるBeitianBN-220Tに交換する必要がありました。その構成ソフトウェアを使用して、必要な「$ GNRMC」NMEAシリアルセンテンスを1秒あたり2回(2Hz)「吐き出す」ように設定してください。 GPSは(TX)シリアルデータをUnoのピン0(RX)に送信します。データには、モーターによる補正の計算に使用されるすべてのナビゲーションデータが含まれています。日付、時刻、位置の緯度と経度、実際のコース、速度、衛星の有効性が修正されます。 ArduinoのIDEプログラミングもピン0(RX)ポートを使用しているため、この操作中はGPSを一時的に切断することを忘れないでください...

私のもう1つの夢は、 EEPROMを使用することでした。 。 IC 2404は、このメモリチップでステッピングモーターの動きのいくつかのパラメータを読み書きするために使用した美しい512バイトのi2c集積回路です。これについては、後で「ソフトウェア」の段落で説明します。

コンポーネントリスト:

  • MCUとしてのArduinoUno
  • WatchDogとしてのArduinoNano
  • Beitian BN-220T GPS
  • ステッピングモーター、モデル23LM、54ステップ=1/4回転
  • モーター用コントローラーキーL298
  • RF433Mhz RC XD-YK04 +4ボタンリモコン+コイルアンテナ
  • 6つのボタンが通常開いています(2x赤、2x緑、1x黒、1x青)
  • 電源のオン/オフの切り替え(白)
  • 外部ステッピングモーター用のメス+オス6ピン丸型コネクタ
  • ブザー
  • LCD16022x16文字+ i2cコンバータ回路を表示
  • 3つのLED(赤、青、黄色))
  • IC 24c04 i2c eeprom
  • IC4051マルチプレクサ
  • バッテリーLiPo2s 7.4v 2600mA
  • IC7805電圧レギュレーター+放熱器
  • サーミスタNTCMF52-10310k
  • リセット可能なヒューズ2A
  • 6x 1N4148ダイオード(D1-D6)
  • 電源シールド上の抵抗(R1-R4 =10k、R5 =100k)
  • オートパイロットシールドの抵抗器(R1 =330、R2 =1k、R3 =2k、R4 =5.1k、R5 =1k、R6 / R7 / R14 =330、R8-R13 =10k、R15 =10M)
  • >
  • コンデンサ(C1 =470uF 16v、C2 =100n)
  • 2W 0.22オーム抵抗(R6)
  • オスピン
  • メスのロングピンヘッダー
  • ケースは透明で「防水」です

いくつかのセンサーがあります IC4051マルチプレクサの助けを借りてすべてArduinoUnoに接続された回路上 。 サーミスタです 電圧レギュレーターの加熱散逸器の温度を制御するために、 2W抵抗器 アンペアを消費電力として計算するための分圧器としての4x10k 回路全体の。また、バッテリー電圧 制御下に置かれます:単一の要素が3.3v未満で放電される場合、LiPoは重要であることが知られています。この回路には、1つのパッケージに2つの要素(2S)LiPoがあり、低電圧(7.0v未満)の場合はブザー 短いビープ音で通知されます。電源を切るのに時間がかかりすぎないようにして、すぐに充電してください。 The Leds :1Hzで点滅する黄色は、WatchDogが機能していることを示します。オートパイロットがオンのときは青いものがオンになり、一時停止しているときはオフになります。リモコンのボタンの1つを押すと、赤いLEDが点滅します。

すべての回路は、LiPo 2S 7.4v 2600mA / hバッテリーと IC7805電圧レギュレーターによって供給される5.0vで動作します 。電流は800mAを超えてはなりませんが、通常は100〜450mA程度です。 加熱散逸器を装着してください 。サーミスタをその上に置き、温度が50°Cを超えるとブザーが鳴ります。

<図> <図> <図> <図> <図> <図> <図> <図>

PCBプリント回路基板とアセンブリ:

片面PCB を使用 そのため、回路全体のルートを解決するために、いくつかのワイヤージャンパー(点線のもの)を含める必要がありました。ここにはコンポーネントの面が示されていますが、下にはすべてのファイル、コンポーネント、はんだ面がミラーリングされており、レーザープリンターを使用して「黄色」または「青色」のシートにダウンロードおよび印刷できます。私は黄色のものを使用しましたが、彼らは青い方が良いと言っています(しかし価格はかなり高いです)。印刷するときは、トナー節約設定を無効にすることを忘れないでください。代わりに1200 dpiの解像度を使用して、真っ黒な結果を得ることができます。マジックシートからPCBへのトナー転写プロセスは、ホットアイロンを使用して行われます...両面とコンポーネント面に印刷することで、アイテムの位置を簡単に認識でき、プロジェクトを「プロフェッショナル」にすることもできます。

両方のPCBは、スタックとして一方を他方のArduinoUnoに合わせるサイズになっています。 :最初にパワーユニット、次にオートパイロットユニット全体。

私の選択は、PCB、MCU、RC、モータードライバー回路、バッテリー、GPS、ボタン、スイッチ、ワイヤー、コネクターなど、すべてのものをまとめることでした。いつかそれらを再利用することを考えています。強い>使用済み ヘッダーと 人気のデュポンワイヤー/接続 代わりは。はんだ付けされていない接続は約200あります。これは、予期しない不要な誤動作や回路のさまざまな動作が時々発生する可能性があることを意味します。これは正常な動作です。 提案はすべてをはんだ付けすることです より安定した回路のために!

<図> <図>

パラメータセンサー値の設定と表示:

黒いボタンを押す ボックスの側面で、セットアップモードに入ります;これはアクティブなナビゲーション中にも実行できます。最初に一時停止を入力する必要はありません。ディスプレイの1ページ目には、バッテリー電圧(V =7.83)、消費電力(mA =177)、および消散器付近のサーミスタセンサーの温度(38°C)が表示されます。次のページに入る黒いボタンを何度も押します。 2、3、4、5ページ目には以下のパラメータが表示されており、-1ボタンと+1ボタンを使用してこれらの値を変更できます。 6ページ目には「更新中...」と表示されます。何かを変更すると、値がEEPROMメモリに保存されます。

  • 間隔: つまり、2000ミリ秒は、ステッピングモーターが、ラダースティックを右または左に動かして「H」の方向を「R」のルートに戻すための試行と別の試行の間の時間です。
  • 最小: つまり、2°は、オートパイロットが介入するためのルート外の最小角度です。この値まで、舵は中央位置に安定したままです。
  • 最大: つまり、40°はステッピングモーターによる一度の最大ステアリング変化です。計算が50°の変化に対するものである場合、実際にはステッパーは40°しか移動しません。
  • 係数: つまり、1.50 x°は、一度にステアリングを変更するための係数です。計算が40°の変化に対するものである場合、実際にはステッピングモーターは(40 x 1.50)=60°移動します;

これらのパラメータは、オートパイロットを微調整するために必要です。 セーリングボートに取り付けた場合。応答性、感度、滑らかさは、プーリーの直径、プーリーの数、ステッピングモーターのメインプーリーの直径、ラダーの感度、ラダースティックが接続されている長さなどによって異なります。すべてをインストールして、船上で体験してみましょう。もちろん、すべてのテスト段階で、風が弱く晴れた美しい日を選んでください!

「ライブ」での動作:

あなたは海、湖、または港の周りを航行しています。それはティータイムであり、あなたのコーラとあなたのお気に入りのサンドイッチがポケットの中で待っています。 自動操縦の切り替え オン 衛星GPSを修正して、ノット、時計、機首方位の実際の速度をディスプレイで読み取る必要があります。つまり、H270°(R =進むべきルート、H =実際の機首方位)を度単位で(180°=南、270°を思い出してください)。 =西、360°または0°=北および90°=東)。一時停止モードの場合、RとHの値は同じです(STOPが表示されます)。ステッピングモーターからラダースティックまでのステアリングロープを接続し、青いボタンを押してオートパイロットステアリングを開始します。;この時点で、オートパイロットはR =ルート方向を保持し、H =ヘディングで何が起こるかを制御します。 見出し番号は確実に変更されます 、すでにお話しした気象条件に応じて、ゆっくりまたは速く。次に、自動操縦はR =ルート方向に復元しようとします H値がR値と等しくなるまで、-10°、+ 5°などの修正を行います 。ルートの変更を決定することができ、ユニットの赤と緑のボタン(-1 -10 +1 +10)を使用するか、リモコンを使用して番号を変更できます。 制御を取り戻すため ステアリング 青いボタンを一時停止し、ラダースティックからロープを外して、手で作業を続けます。よくできました。

ソフトウェア側:

コードはかなり長いですが、簡単に理解できるほど明確であることを願っています。いずれにせよ、私はそれがどのように行われているのかを説明します。スケッチはプログラムの約65%とメモリの約45%を使用します。 Stringクラスを使用しても、主にシリアルNMEAセンテンスの操作に使用されますが、エラボレーションフロー全体は安定していて安定しています。 「serialEvent()」を使用して、 GPSから1秒間に2回データを受信します 、「nmeaExtractData()」を呼び出し、最後に「nmea0183_checksum()でデータパケットをチェックして、データの整合性を確認します。別のメーカーとモデルのGPSを使用する場合は、文が同じ構造であるか、ここで変更を加える必要があります。 。たとえば、 EM406Aは「$ GPRMC」パケットIDを使用します 、BT220は代わりに「$ GNRMC」を使用します...名前を少し変更するだけです...便利なリンクがチェックサムテストに役立ちます:https://nmeachecksum.eqth.net-完全なNMEA文の例です。 :ID、時間、有効性、緯度、経度、速度、実際のコース、日付、バリエーション、チェックサム。

$ GPRMC、095836.000、A、4551.9676、N、01328.7118、E、2.09、341.84、280519 、、 * 08

「Setup()」中に EEPROMがチェックされます :新規または不明の場合、初期化(フォーマット)されます。メモリ内のパラメータはバイトとして読み取り/書き込みされます:0 =0x29、1 =0x00、2-3 =間隔、4-5 =最小、6-7 =最大、8-11 =係数(バイト、バイト、int、int、浮く)。 EEPROM r / w操作を慎重に処理しましたが、防御が多すぎる可能性があります... センサーは10秒ごとにチェックされます マルチプレクサによる「readMuxSensors()」を介して、バッテリーが少ないか温度が高い場合にアラームを引き起こす可能性があります。消費電力の分解能は低く、約40mAのステップです。 ハードウェアとRCボタン 継続的にチェックされます。それらが何をするかは、「IsSetup」ブール値と「RefreshDisplay()」の表示によって異なります。 。コードのコアは、「gomotor()」関数を呼び出してステッパーを出し入れするためのSTEERINGCONTROLセクションです。;はい、ラダーを右に10°移動し、間隔値の後にゼロラダー位置に戻る可能性があります。以下同様に、新しい計算ラウンドの後に実行されます。すでに述べたように、ステアリング作業は、いくつかのボタンと表示動作に影響を与えるため、セットアップ中にも行われます。 ワットドッグフィーディング 非常にシンプルですが重要です。できるだけ早くピンをオン/オフするだけです。

帆船に取り付ける方法:

下の写真に示されているように、私はオートパイロットとステッピングモーターを船尾に配置することを選択しました。どちらもボルトなどでしっかりと固定されています。直径6mmのロープは、メインのモータープーリーから始まり、両側に配置された他の2つのプーリーを一周します。これらの2つの滑車は、ロープの張力をわずかに保つために、バンジーの2つのリングを介してボートに「固定」する必要があります。この時点で、最後に、ロープをラダースティックに接続する方法を決定する必要があります(一時的な接続)。オートパイロットが動作している間は接続する必要があり、接続と切断が簡単です。オートパイロットシステムを水から遠ざけてください! :-)

<図>

ニュースとアップデート:

  • 10.05.2020、ステッパープーリー(私による)と取り付けプレート(Andrew Barneyによる)の.STEP 3D CADプロジェクトファイル、およびそれらの3Dプレビュー画像をダウンロードするために追加されました。

免責事項と警告:

これは私たちがここでプレイしているゲームであり、何もないとしましょう から 真剣に!数年前、私は帆船に乗って世界中を16ヶ月という長い旅に出ました。実際の自動操縦で広範囲にナビゲートしました( これ ONE!) すべての気象条件で、悪天候でも。実際の自動操縦は何かです 非常に 強い ハードウェアとソフトウェアの両方信頼する必要があります たくさん。代わりにこのArduinoオートパイロット はプレイするのに素晴らしいゲームです with and to 楽しみのために時間を費やしてください。

楽しんでください!

マルコゾンカ

コード

  • 自動操縦スケッチ(宇野用)
  • WatchDogスケッチ(Nano用)
オートパイロットスケッチ(Uno用) Arduino
 / *このスケッチは、小型帆船の自動操縦として機能します。MarcoZonca、2019 Arduino UNOをCPU、Arduino Nanoをウォッチドッグ、GPS BT-220 nmea、ステッパーモーター+コントローラー、rf433Mhz RC、6ボタン、ブザー、i2cディスプレイ、2リード、i2c 24c04 eeprom、センサー用Mux 4051、lipo 2s 7.4v 2600mA、7805電圧レギュレーター、サーミスター; * /#include  #include  #include # include  String inputString =""; String nm_time ="00:00:00"; String nm_validity ="V"; String nm_latitude ="ddmm.mmmm'N"; String nm_longitude ="dddmm.mmmm'E "; String nm_knots =" 0.0kn "; float nmf_knots =0.0; String nm_truecourse =" 360 "; float nmf_truecourse =360; String nm_date =" dd / mm / yyyy "; String nm_routetofollow =" 000 "; float nmf_routetofollow =0; unsigned long previousStearingMillis =0; unsigned long currentStearingMillis =0; unsigned long prevCheckSensorsMillis =0; unsigned long currCheckSensorsMillis =0; int CheckSensorsInterval =10000; bool stringComplete =false; bool isfirstfix =t rue; bool ispause =true; bool isStearing =false; bool isSetup =false; int s =0; int y =0; int z =0; int d =0; int rfRemoteControlValue =0; int HWButtonValue =0; int SetupParameter =0; float calcmove =0; float cm =0; float Stearing =0; float prevStearing =0; float t =0; int EEdisk =0x50; int EEid1 =0x29; int EEid2 =0x00; unsigned int EEaddress =0; unsigned int EEbytes =12; byte EEdata [12]; byte EEbytedata; int EEerr =0; float SensorVBatt =0; float SensorVRes =0; float SensorTemp =0; float SensormAmp =0; //以下のパラメーターはデフォルトですが、読み取り/書き込み可能ですeeprom // eepromは、アドレス0と1で内容が異なる場合に初期化されます。アドレスlenタイプnotes // 0x50 EEdiskで0〜255バイト、0x51で256〜512バイト(未使用)--------- -------------------------------------------------- ---- // 0 1Bバイト01001001(自動パイロットプロジェクトID1として0x29)// 1 1Bバイト00000000(0x00 "" id2)int StearingInterval =2000; //試行と戻るの間のミリ秒22B int StearingInterval1000-5000ステップ100intStearingMinToMove =2; // compass_degrees 4 2B int StearingMinToMove0-20ステップ1intStearingMaxMove =40; // compass_degrees 6 2B int StearingMaxMove10-45ステップ1floatStearingCoeffMove =1.5; //(compass_degrees * coeff)として使用8 4B float StearingCoeffMove 0.1〜4ステップ0.1 // 12 free // byte bStearingInterval [sizeof(int)]; byte bStearingMinToMove [sizeof(int)]; byte bStearingMaxMove [sizeof(int)]; byte bStearingCoeffMove [sizeof(float)]; int prev_StearingInterval =0; int prev_StearingMinToMove =0; int prev_StearingMaxMove =0; float prev_StearingCoeffMove =0; const int ledpausePin =2; const int watchDogPin =3; const // 00 =Vin 01 =Vbatt 10 =Tempconst int MuxSelBit1Pin =6; // const int motorsABenablePin =13; const int MuxIOPin =14; const int ButtonsPin =15; const int rfRemoteControlPin =16; const int SpeakerPin =17; const int RCleftbutton =201; const int RCrightbutton =202; const int RCleft10button =203; const int RCright10button =204; const int HWleftbutton =101; const int HWrightbutton =102; const int HWpausebutton =103; const int HWsetupbutton =104; const int HWleft10button =105; const int HWright10button =106; const int motorStepsPerRevolution =200; //モデル23LMの場合は200、54ステップ=回転の1 / 4LiquidCrystal_I2C lcd(0x27、2、1、0、4、5、6、7、3、POSITIVE);ステッピングモーター(motorStepsPerRevolution、9、10、11、12 ); void setup(){Serial.begin(4800); lcd.begin(16,2); Wire.begin(); motor.setSpeed(60); inputString.reserve(200); pinMode(motorsABenablePin、OUTPUT); pinMode(MuxSelBit0Pin、OUTPUT); pinMode(MuxSelBit1Pin、OUTPUT); digitalWrite(motorsABenablePin、LOW); digitalWrite(MuxSelBit0Pin、LOW); digitalWrite(MuxSelBit1Pin、LOW); pinMode(ledpausePin、OUTPUT); pinMode(watchDogPin、OUTPUT); digitalWrite(ledpausePin、LOW); digitalWrite(watchDogPin、LOW); // EEPROMの読み取りとチェック(新しい(または識別されていない)場合のフォーマット)lcd.clear(); lcd.setCursor(0,0); lcd.print( "メモリチェック..."); lcd.setCursor(0,1); for(s =0; s  =CheckSensorsInterval){readMuxSensors(); if((SensorVBatt <=7.0)||(SensorTemp>
 =50)){lcd.clear(); lcd.setCursor(0,0); lcd.print( "アラームセンサー!"); lcd.setCursor(1,1); lcd.print( "V ="); lcd.print(SensorVBatt); lcd.print( ""); lcd.print(int(SensorTemp)); lcd.write(0xDF); lcd.print( "C"); NewTone(speakerPin、10); delay(1000); noNewTone(); } prevCheckSensorsMillis =currCheckSensorsMillis; } //ステアリングコントロール---------------- currentStearingMillis =millis(); if(currentStearingMillis --previousStearingMillis> =StearingInterval){if(isStearing ==false &&ispause ==false){//試してみる(move stearing)calcmove =nmf_routetofollow --nmf_truecourse; if(calcmove <(-180)){calcmove =calcmove + 360; } else {if(calcmove>(+ 180)){calcmove =calcmove --360; }} if(abs(calcmove)> =StearingMinToMove){if(abs(calcmove)> =StearingMaxMove){if(calcmove <0){cm =(StearingMaxMove * -1); calcmove =cm; } else {cm =(StearingMaxMove * 1); calcmove =cm; }} Stearing =(calcmove * StearingCoeffMove); gomotor(int((Stearing * 216)/ 360)); // 54ステップ=回転の1 / 4prevStearing =Stearing; isStearing =true; }} else {//戻る(ステアリングを「ゼロ」位置に移動)if(isStearing ==true){Stearing =(prevStearing * -1); gomotor(int((Stearing * 216)/ 360)); // 54ステップ=回転の1/4ステアリング=0; prevStearing =0; isStearing =false; }} previousStearingMillis =currentStearingMillis; } // RCRFボタン------------------ rfRemoteControlValue =checkRfRC(); if(rfRemoteControlValue){switch(rfRemoteControlValue){case RCleftbutton://左RCボタンgoleft();壊す; case RCrightbutton://右RCボタンgoright();壊す; case RCleft10button://左-10 RCボタンgoleft10();壊す; case RCright10button://右+10 RCボタンgoright10();壊す; }} //ボタン------------------------ HWButtonValue =checkHWButtons(); if(HWButtonValue){switch(HWButtonValue){case HWleftbutton:// Left(-1)HWボタンif(isSetup ==false){goleft(); } else {setupMinus(); } 壊す; case HWrightbutton:// Right(+1)HWボタンif(isSetup ==false){goright(); } else {setupPlus(); } 壊す; case HWpausebutton:// HWボタンを一時停止gopause();壊す; case HWsetupbutton:// HWボタンを設定gosetup();壊す; case HWleft10button:// Left(-10)HWボタンgoleft10();壊す; case HWright10button:// Right(+10)HWボタンgoright10();壊す; }} // GPS NMEA ------------------ if(stringComplete ==true){//シリアルポートRX boolretでnmeaセンテンスを受信しました; ret =nmeaExtractData(); inputString =""; stringComplete =false; if(ret ==true){RefreshDisplay(); }} // WATCHDOG FEEDING ---------------- if(digitalRead(watchDogPin)==LOW){digitalWrite(watchDogPin、HIGH); } else {digitalWrite(watchDogPin、LOW); }} //マルチプレクサでセンサーを読み取るvoidreadMuxSensors(){float Vo =0; float n =0; float n1 =0; float v1ad =0; float v2ad =0; float corr =0;フロートR1 =10000; float logR2 =0;フロートR2 =0;フロートT =0; float c1 =1.009249522e-03; float c2 =2.378405444e-04; float c3 =2.019202697e-07; digitalWrite(MuxSelBit0Pin、LOW); // 00 =Vbatt digitalWrite(MuxSelBit1Pin、LOW); n =analogRead(MuxIOPin); v1ad =n; n1 =(((10.00 * n)/ 1023.00)); SensorVBatt =(n1 +((n1 * 0.0)/ 100)); //任意の修正(非アクティブ=0.0%)digitalWrite(MuxSelBit0Pin、LOW); // 01 =Vres digitalWrite(MuxSelBit1Pin、HIGH); n =analogRead(MuxIOPin); v2ad =n; n1 =(((10.00 * n)/ 1023.00)); SensorVRes =(n1 +((n1 * 0.0)/ 100)); //任意の修正(非アクティブ=0.0%)digitalWrite(MuxSelBit0Pin、HIGH); // 10 =NTC Temp digitalWrite(MuxSelBit1Pin、LOW); Vo =analogRead(MuxIOPin); R2 =R1 *(1023.0 / Vo-1.0); logR2 =log(R2); T =(1.0 /(c1 + c2 * logR2 + c3 * logR2 * logR2 * logR2)); SensorTemp =T-273.15; //摂氏n =(v1ad-v2ad); n1 =(n / 0.22)* 1000.00; SensormAmp =(((10.00 * n1)/ 1023.00));} // nmea inputStringbool nmeaExtractData(){bool ret =false;からデータを抽出// nmeaセンテンス=$ GNRMCおよび有効なCHKSUMの場合はtrueif((inputString.substring(0,6)=="$ GNRMC")&&(inputString.substring(inputString.length()-4、inputString.length()- 2)==nmea0183_checksum(inputString))){y =0; for(s =1; s <11; s ++){y =inputString.indexOf( "、"、y); switch(s){ケース1:// time z =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nm_time =inputString.substring(y + 1、y + 2 + 1)+ ":" + inputString.substring(y + 1 + 2、y + 4 + 1)+ " : "+ inputString.substring(y + 1 + 4、y + 6 + 1); } y =z;壊す;ケース2://有効性z =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nm_validity =inputString.substring(y + 1、y + 1 + 1); } y =z;壊す;ケース3://緯度z =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nm_latitude =inputString.substring(y + 1、y + 2 + 1)+ "" + inputString.substring(y + 1 + 2、y + 10 + 1)+ "' "; } y =z;壊す;ケース4:// north / south z =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nm_latitude =nm_latitude + inputString.substring(y + 1、y + 1 + 1); } y =z;壊す;ケース5://経度z =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nm_longitude =inputString.substring(y + 1、y + 3 + 1)+ "" + inputString.substring(y + 1 + 3、y + 11 + 1)+ "' "; } y =z;壊す;ケース6:// east / west z =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nm_longitude =nm_longitude + inputString.substring(y + 1、y + 1 + 1); } y =z;壊す;ケース7://速度ノットz =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nmf_knots =inputString.substring(y + 1、z).toFloat(); t =roundOneDec(nmf_knots); nm_knots =String(t、1)+ "kn"; } y =z;壊す;ケース8:// trueコースz =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nmf_truecourse =inputString.substring(y + 1、z).toFloat(); d =nmf_truecourse; nm_truecourse =d; } y =z;壊す;ケース9:// date z =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nm_date =inputString.substring(y + 1、y + 2 + 1)+ "/" + inputString.substring(y + 1 + 2、y + 4 + 1)+ " / 20 "+ inputString.substring(y + 1 + 4、y + 6 + 1); } y =z;壊す;ケース10://ステートメントが壊れます;デフォルト://ステートメントブレーク; }} if((isfirstfix ==true)||(ispause ==true)){nm_routetofollow =nm_truecourse; nmf_routetofollow =nmf_truecourse; isfirstfix =false; } ret =true; } return ret;} // setupvoid setupPlus(){switch(SetupParameter){case 2:// interval StearingInterval =(StearingInterval + 100);中のincrease(+)パラメーター値if(StearingInterval> 5000){StearingInterval =5000; } 壊す;ケース3://分。 StearingMinToMove =(StearingMinToMove + 1);を移動します。 if(StearingMinToMove> 20){StearingMinToMove =20; } 壊す;ケース4://最大。移動StearingMaxMove =(StearingMaxMove + 1); if(StearingMaxMove> 45){StearingMaxMove =45; } 壊す;ケース5://係数StearingCoeffMove =(StearingCoeffMove + 0.1); if (StearingCoeffMove> 4) { StearingCoeffMove =4; } break; } delay(200); RefreshDisplay();}// decrease(-) parameter value during setupvoid setupMinus() { switch (SetupParameter) { case 2://interval StearingInterval =(StearingInterval - 100); if (StearingInterval <1000) { StearingInterval =1000; } break; case 3://min. to move StearingMinToMove =(StearingMinToMove - 1); if (StearingMinToMove <0) { StearingMinToMove =0; } break; case 4://max. move StearingMaxMove =(StearingMaxMove - 1); if (StearingMaxMove <10) { StearingMaxMove =10; } break; case 5://coefficient StearingCoeffMove =(StearingCoeffMove - 0.1); if (StearingCoeffMove <0.1) { StearingCoeffMove =0.1; } break; } delay(200); RefreshDisplay();}// motor control (+)=forward (-)=backwardsvoid gomotor(int stepsToMove) { digitalWrite(motorsABenablePin, HIGH); motor.step(stepsToMove); digitalWrite(motorsABenablePin, LOW);}// refresh data on displayvoid RefreshDisplay() { if (isSetup ==false) { //---------normal lcd.clear(); lcd.setCursor(0,0); lcd.print("R"+nm_routetofollow); lcd.write(0xDF); lcd.print(" H"+nm_truecourse); lcd.write(0xDF); if (ispause ==true) { lcd.print(" STOP"); } else { if (Stearing> 0) { lcd.print(" +"); } if (Stearing ==0) { lcd.print(" "); } if (Stearing <0) { lcd.print(" "); } lcd.print(int(Stearing)); } lcd.setCursor(0,1); lcd.print(nm_time+" "+nm_knots); } if (isSetup ==true) { //-----------setup lcd.clear(); lcd.setCursor(0,0); lcd.print("setup:"); switch (SetupParameter) { case 1://display sensors readMuxSensors(); lcd.print("V="); lcd.print(SensorVBatt); lcd.setCursor(1,1); lcd.print("mA="); lcd.print(int(SensormAmp)); lcd.print(" "); lcd.print(int(SensorTemp)); lcd.write(0xDF); lcd.print("C");壊す; case 2://interval lcd.print("interval"); lcd.setCursor(7,1); lcd.print(StearingInterval); lcd.print(" mSec");壊す; case 3://min. to move lcd.print("minimum"); lcd.setCursor(7,1); lcd.print(StearingMinToMove); lcd.write(0xDF);壊す; case 4://max. move lcd.print("max"); lcd.setCursor(7,1); lcd.print(StearingMaxMove); lcd.write(0xDF);壊す; case 5://coefficient lcd.print("coeffic."); lcd.setCursor(7,1); lcd.print(StearingCoeffMove); lcd.print(" x "); lcd.write(0xDF);壊す; } }}/* SerialEvent occurs whenever a new data comes in the hardware serial RX. This routine is run between each time loop() runs, so using delay inside loop can delay response. Multiple bytes of data may be available.*/void serialEvent() { while (Serial.available()) { char inChar =(char)Serial.read(); inputString +=inChar; // if the incoming character is a newline, set a flag so the main loop can // do something about it if (inChar =='\n') { stringComplete =true; } } }//calculate checksum of nmea sentenceString nmea0183_checksum(String nmea_data) { int crc =0; String chSumString =""; int i; // ignore the first $ sign, checksum in sentence for (i =1; i <(nmea_data.length()-5); i ++) { // remove the - 5 if no "*" + cksum + cr + lf are present crc ^=nmea_data[i]; } chSumString =String(crc,HEX); if (chSumString.length()==1) { chSumString="0"+chSumString.substring(0,1); } chSumString.toUpperCase(); return chSumString;}//check RC which button is pressedint checkRfRC() { int n =0; int res =0; n =analogRead(rfRemoteControlPin); if ((n>350) and (n<460)) { // button A res =RCleftbutton; } if ((n> 90) and (n<190)) { // button B res =RCrightbutton; } if ((n>540) and (n<640)) { // button C res =RCleft10button; } if ((n>225) and (n<325)) { // button D res =RCright10button; } return res; }//check HW which button is pressedint checkHWButtons() { int n =0; int res =0; n =analogRead(ButtonsPin); //Serial.println(n); if ((n>465) and (n<565)) { // button left res =HWleftbutton; } if ((n>290) and (n<390)) { // button right res =HWrightbutton; } if ((n>130) and (n<220)) { // button pause res =HWpausebutton; } if ((n>625) and (n<725)) { // button setup res =HWsetupbutton; } if ((n>975) and (n<1075)) { // button left-10 res =HWleft10button; } if ((n>800) and (n<900)) { // button right+10 res =HWright10button; } return res; }void gosetup() { // setup button if (isSetup ==false) { SetupParameter =1; isSetup =true; } else { if (SetupParameter <5) { SetupParameter ++; } else { if (prev_StearingInterval !=StearingInterval || prev_StearingMinToMove !=StearingMinToMove || prev_StearingMaxMove !=StearingMaxMove || prev_StearingCoeffMove !=StearingCoeffMove) { lcd.clear(); lcd.setCursor(0,0); lcd.print("updating... "); delay(1000); goupdateEEPROM(); if (EEerr) { lcd.print("E="); lcd.print(EEerr); delay(1000); } prev_StearingInterval =StearingInterval; prev_StearingMinToMove =StearingMinToMove; prev_StearingMaxMove =StearingMaxMove; prev_StearingCoeffMove =StearingCoeffMove; } isSetup =false; } } NewTone (speakerPin,2000); delay(200); noNewTone(); RefreshDisplay();}void goupdateEEPROM() { EEaddress =0; //id1 EEdata[0] =EEid1; EEbytedata =EEid1; writeEEPROM (EEdisk, EEaddress, EEbytedata); EEaddress =1; //id2 EEdata[1] =EEid2; EEbytedata =EEid2; writeEEPROM (EEdisk, EEaddress, EEbytedata); memcpy(bStearingInterval, &StearingInterval, sizeof(int)); memcpy(bStearingMinToMove, &StearingMinToMove, sizeof(int)); memcpy(bStearingMaxMove, &StearingMaxMove, sizeof(int)); memcpy(bStearingCoeffMove, &StearingCoeffMove, sizeof(float)); memcpy(EEdata+2,bStearingInterval,sizeof(int)); memcpy(EEdata+4,bStearingMinToMove,sizeof(int)); memcpy(EEdata+6,bStearingMaxMove,sizeof(int)); memcpy(EEdata+8,bStearingCoeffMove,sizeof(float)); for (s =2; s  360) { nmf_routetofollow =1; } d=nmf_routetofollow; nmf_routetofollow=d; nm_routetofollow=d; NewTone (speakerPin,800); delay(200); noNewTone(); } else { NewTone (speakerPin,1000); delay(50); noNewTone(); } RefreshDisplay();}void goright10() { // right 10x button/RC if (ispause ==false) { for (s =1; s <11; s ++) { nmf_routetofollow ++; if (nmf_routetofollow> 360) { nmf_routetofollow =1; } } d=nmf_routetofollow; nmf_routetofollow=d; nm_routetofollow=d; NewTone (speakerPin,800); delay(200); noNewTone(); } else { NewTone (speakerPin,1000); delay(50); noNewTone(); } RefreshDisplay();}void gopause() { // pause button/RC if (ispause ==true) { ispause=false; digitalWrite(ledpausePin, HIGH); NewTone (speakerPin,50); delay(200); NewTone (speakerPin,200); delay(800); noNewTone(); } else { ispause=true; digitalWrite(ledpausePin, LOW); NewTone (speakerPin,200); delay(200); NewTone (speakerPin,50); delay(800); noNewTone(); } RefreshDisplay();}// reading eeprombyte readEEPROM (int diskaddress, unsigned int memaddress) { byte rdata =0x00; Wire.beginTransmission (diskaddress); Wire.write (memaddress); if (Wire.endTransmission () ==0) { Wire.requestFrom (diskaddress,1); if (Wire.available()) { rdata =Wire.read(); } else { EEerr =1; //"READ no data available" } } else { EEerr =2; //"READ eTX error" } Wire.endTransmission (true); return rdata;}// writing eepromvoid writeEEPROM (int diskaddress, unsigned int memaddress, byte bytedata) { Wire.beginTransmission (diskaddress); Wire.write (memaddress); Wire.write (bytedata); if (Wire.endTransmission () !=0) { EEerr =3; //"WRITING eTX error" } Wire.endTransmission (true); delay(5); }// round zero decimalfloat roundZeroDec(float f) { float y, d; y =f*1; d =y - (int)y; y =(float)(int)(f*1)/1; if (d>=0.5) { y +=1; } else { if (d <-0.5) { y -=1; } } return y;}// round one decimalfloat roundOneDec(float f) { float y, d; y =f*10; d =y - (int)y; y =(float)(int)(f*10)/10; if (d>=0.5) { y +=0.1; } else { if (d <-0.5) { y -=0.1; } } return y;}// round two decimalfloat roundTwoDec(float f) { float y, d; y =f*100; d =y - (int)y; y =(float)(int)(f*100)/100; if (d>=0.5) { y +=0.01; } else { if (d <-0.5) { y -=0.01; } } return y;}
WatchDog sketch (for Nano)Arduino
/* * This sketch is a Watchdog to keep CLIENT under control, on Arduino NANO 3.0 by Marco Zonca * CLIENT must feed Whatcdog sooner then feedingInterval otherwise will be forced to restart * */const int feedingPin =14;const int ledPin =15;const int restartPin =16;const int buzzerPin =17;const long ledInterval =1000;const long feedingInterval =2500;const long timeForClientStart =16000;int ledState =LOW;int previousFeedingState =LOW;int feedingState =LOW;unsigned long previousLedMillis =0;unsigned long previousFeedingMillis =0;void setup() { digitalWrite(restartPin, HIGH); // LOW will force CLIENT to restart pinMode(ledPin, OUTPUT); pinMode(buzzerPin, OUTPUT); pinMode(restartPin, OUTPUT); pinMode(feedingPin, INPUT); delay(timeForClientStart); // let time to CLIENT to start...}void loop() { unsigned long currentMillis =millis(); // BLINK LED ------------------- if (currentMillis - previousLedMillis>=ledInterval) { previousLedMillis =currentMillis; if(ledState ==LOW){ledState =HIGH; } else {ledState =LOW; } digitalWrite(ledPin、ledState); } // CHECK THE FEEDING ------------------- feedingState =digitalRead(feedingPin); // CLIENT must set pin HIGH -> LOW frequently to prove it's alive if (feedingState ==HIGH) { if (previousFeedingState ==LOW) { previousFeedingMillis =currentMillis; } previousFeedingState =HIGH; } else { previousFeedingState =LOW; } if (currentMillis - previousFeedingMillis> feedingInterval) { // CLIENT is sleeping ledState =HIGH; digitalWrite(ledPin, ledState); tone(buzzerPin,1500); delay(500); digitalWrite(restartPin, LOW); //restart CLIENT tone(buzzerPin,1500); delay(500); digitalWrite(restartPin, HIGH); tone(buzzerPin,1500); delay(timeForClientStart); // let CLIENT time to restart... noTone(buzzerPin); currentMillis =millis(); previousFeedingState =LOW; previousFeedingMillis =currentMillis; previousLedMillis =currentMillis; }} 

カスタムパーツとエンクロージャー

23lm-stepper-plate-v2_PlvJaff9Hl.step 23lm-stepper-pulley-56_UhsbaWbiBt.step

回路図


製造プロセス

  1. ラズベリーパイ用DIY赤外線モーションセンサーシステム
  2. ドローン用の緊急着陸用パラシュート回収システムを構築する
  3. 継続的な監視システムはあなたに適していますか?
  4. ロボット用自動工具交換装置
  5. システムはIoTの準備ができていますか?
  6. 自動旋盤用ポリゴンカッター
  7. 精密部品用CNC自動旋盤
  8. 極超音速飛行のための推進システム
  9. 手動および自動変速機システムの動作原理
  10. オートマチックトランスミッションシステムを理解する
  11. 自動潤滑システムを理解する