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

位置フィードバック付き高精度リニアサーボアクチュエーターの自作ガイド

概要

このチュートリアルでは、リニアサーボアクチュエーターの自作方法を学びます。通常のリニアアクチュエーターは電圧印加で一方向へ動くだけですが、この自作版はフィードバックループシステムを備え、正確で再現性のある動きを簡単に制御できます。

「サーボアクチュエーター」と呼ばれる所以は、出力動作を精密に制御するフィードバックループを持つからです。

入力モード

アナログ入力: 線形ポテンショメーター、一般的なロータリーポテンショメーター、ジョイスティック(これもロータリータイプ)など、あらゆるタイプのポテンショメーターが使用可能。

デジタル入力: RC送信機で制御可能。RC受信機をアクチュエーターの入力として接続。

両モードとも接続はわずか3線(電源2線+信号1線)で完結します。

主な機能

動作原理

クローズドループ制御は、AS5600磁気ロータリーポジションセンサーとDCモーター駆動用PID制御に基づきます。前回動画で製作したカスタムサーボモーターコントローラーボード(独自マイコン内蔵)を流用し、任意のDCモーターをスタンドアローンサーボモーター化しています。

サーボモーターの原理:入力信号(目標位置)と位置フィードバックセンサーからの実位置を比較。差分(エラー)をコントローラーが処理し、目標位置に到達するまでモーターを駆動。このリニアサーボアクチュエーターは、リードスクリュー機構でモーターの回転運動を直線運動に変換するステップが追加されています。

設計詳細

位置検出: AS5600センサーをアクチュエーター後部に配置、リードスクリューの回転を追跡。リードスクリューピッチ8mm(1回転で8mm直線移動)。AS5600は12ビットエンコーダー(4096位置/回転)。分解能 = 8mm ÷ 4096 = 0.001953mm。極めて高精度。

モーターと速度: 12Vギアボックス内蔵DCモーター(出力480RPM)。480÷60=8回転/秒 × 8mmピッチ = 64mm/秒の直線速度。最大ストローク150mmなので、最高速で約2.5秒(加減速込みで約3秒)で全行程。リードスクリュー駆動ギア比は1:1としました。

機構設計: カスタムサーボコントローラーPCB(40×40mm)とリードスクリューナット(外径22mm)サイズに基づきシリンダーブロックを設計。ナットとロッドはM3ボルト4本とスレッドインサートで接続。ロッド先端にベアリング(シリンダーブロック上をスライド)で回転防止ガイド。出力側シリンダーキャップに小型ベアリング4個でロッドをガイド。シリンダーブロック内部にマイクロリミットスイッチを内蔵し、ホーミング/開始位置設定に使用。

3Dモデルダウンロード

Onshapeでブラウザ閲覧・探索可能(無料アカウントで利用可)。STLファイルとSTEPファイルはCults3Dからダウンロード。シリンダーブロックとロッドを延長するだけで最大ストロークを簡単に延長可能。今回は220×220mmベッドのプリンターに収まる寸法に設定(最長パーツ:ロッド215mm)。

3Dプリント

Creality Ender-3 V3 SEでY軸方向水平プリント。この方向でプリントすると動作が滑らかになりロッド強度も向上。水平展開機能でフィラメント膨張を補正し寸法精度を確保。-0.1mmを使用(テストプリントで最適値を決定)。シリンダーブロックはZ軸方向でプリントしサポート材を最小化。このプリンターは200ドル以下で自動レベリング、ダイレクトエクストルーダー、250mm/s高速プリント、高品質を実現するコスパ最強機種。詳細レビューもご参照ください。

組み立て

部品リスト

機械部品

電子部品

注: アフィリエイトリンクを含みます。

リードスクリュー機構の組み立て

  1. シリンダーベースブロックに3Dプリントナット(リードスクリューと同ネジ)をねじ込む。タイトフィットで保持力確保。ナットにはグリブネジ用スレッドインサート穴あり。
  2. リードスクリューをベースブロックの2個のベアリング(外径22mm)で保持。
  3. 後部に駆動ギアを取り付け(ナット同様にネジ込み、グリブネジ固定)。モータートルクを伝達する重要な接続。
  4. ギアとナットにスレッドインサートを入れ、ベースブロックにもインサート設置。ギアとナットを逆方向にねじ込み(ベアリングへのアキシアル荷重を避けるため締めすぎ注意)、グリブネジで固定。
  5. プレートをベースブロックに取り付けベアリングを保持。リードスクリューサブアセンブリ完成。
  6. ロッド準備:中空ロッドにスレッドインサート設置。先端にガイドベアリング(外径13mm、内径6mm)を3Dプリント6mm中空シャフト経由でM3皿ネジ10mmで固定。
  7. シリンダーをベースブロックに取り付け。事前にマイクロリミットスイッチを設置(NC接続でハンダ付け、配線約15cm、シリンダー上部の穴を通しM2×8mmボルト2本で固定。ガイドベアリングが適切なタイミングで作動する専用モデル推奨)。
  8. スレッドインサート経由でM4×25mmボルト2本(上側のみ)でシリンダー固定。下側2本は後でギアボックス/PCBカバーと共用。
  9. リードスクリューにロッドをねじ込み、ガイドベアリングをシリンダーガイドレール間に挿入。後部ギアを回すとロッドが後退しリミットスイッチまで移動。
  10. シリンダーキャップ取り付け:外径9mmベアリング4個(3Dプリントシャフト用)を収容。シャフト挿入部は薄く破損しやすいので慎重に。ベアリングでロッドを支持・ガイド。M4ボルト4本で固定。

DCモーター取り付け

  1. DCモーターをM3ボルト6本で固定。
  2. モーターシャフトにギア取り付け、スレッドインサートとグリブネジで固定。
  3. ギア噛み合い確認後、ギアボックス/PCBカバーを後部に取り付け。ベースブロックに追加スレッドインサート設置。
  4. DCモーター配線をハンダ付け(約20cm)。ベースブロックの穴を通し、カバーのホルダー2つでギアから離して配線。
  5. カバー固定:下側M4ボルト2本を先に少し挿入(2-3mm残す)、カバーホルダーを挟み込んでから締め付け。PCBホルダーがボルト経路を塞ぐためこの手順に。

カスタムサーボコントローラーPCB取り付け

前回動画と同一のコントローラーボードを使用。メインコンポーネントはAS5600磁気ロータリーエンコーダーで、出力シャフトのマグネット位置を追跡。ここではリードスクリュー出力ギアにマグネットを取り付け、PCB装着時にAS5600と完璧にアラインメント。

PCB固定:ホルダースロットにM2ナット挿入、M2ボルト4本で締め付け。配線接続:DCモーター線をモーターターミナルブロックへ(極性確認必須)。接続前にお試し電圧印加でリードスクリュー機構の動作確認推奨。リミットスイッチ線:専用ピンがないため、GND線を電解コンデンサーのGNDパッドへ、NC線をSCKピン(ATmega328のデジタルピン13)へハンダ付け。電源ターミナルブロックはカバー側面近くにあり、5.5mm電源コネクター用穴を用意。DCモータードライバーにヒートシンク追加。最後にスナップフィット蓋を閉じて完成。

任意のポテンショメーターまたはRC受信機を適切な入力ピンに接続して位置制御可能。コントローラーの詳細(回路図、PCB製作)は前回動画を参照。

回路図

メインはAS5600磁気センサー(アクチュエーター出力位置追跡)。センサーデータ→ATmega328マイコン(演算)→DRV8871ドライバー(DCモーター駆動)。DRV8871はピーク3.6A対応。12V入力をAMS1117で5V降圧しマイコン等へ供給。2ch DIPスイッチで入力モード選択(アナログ/デジタル/シリアル通信)。トリマーポテンショメーターで感度調整、タクトスイッチで開始/終了位置設定。

PCBWayで発注:4層基板(中間層GND)、色ホワイト、表面処理イマージョンゴールド(無料なら受け入れ)。ガーバーファイルはPCBWayプロジェクト共有から。

このカスタムコントローラーなしでも、AS5600ブレークアウトボード+Arduinoで制御可能。

コード解説

/* Linear Servo Actuator - Arduino Code by Dejan, www.HowToMechatronics.com
Libraries: AS5600 encoder: https://github.com/RobTillaart/AS5600
PID controller: https://github.com/br3ttb/Arduino-PID-Library/blob/master/PID_v1.h */
#include "AS5600.h"
#include "Wire.h"
#include 
AS5600 as5600; // use default Wire
double Pk1 = 0.65; // 到達速度
double Ik1 = 0;
double Dk1 = 0.1;
// PID変数
double Setpoint, Input, Output;
PID myPID(&Input, &Output, &Setpoint, Pk1, Ik1, Dk1, DIRECT);
#define motor_IN1 5
#define motor_IN2 6
#define ch1 2
#define setButton 7
#define inputSwitch 3
#define modeSwitch 4
#define limitSwitch 13
int ch1Value;
long encoderValue, desiredValue, pwmValue;
String serialInput = "";
int serialIntInput;
double totalDistance = 0;
long startPosition = 358;
long endPosition = 6750;
long rangeAdjustment = 0;
float sensitivityAdjustment = 0;
float angle = 0;
float angleValue = 0;
float rodPosition;
float positionsArray[100];
int positionsCounter = 0;
long quadrantNumber = 2;
long previousQuadrantNumber = 3;
long numberOfTurns = 0;
float totalAngle = 0;
char incomingByte = 0;
int intInput = 0;

void setup() {
  Serial.begin(115200);
  Serial.println(__FILE__);
  Serial.print("AS5600_LIB_VERSION: ");
  Serial.println(AS5600_LIB_VERSION);
  Wire.begin();
  pinMode(motor_IN1, OUTPUT);
  pinMode(motor_IN2, OUTPUT);
  pinMode(setButton, INPUT_PULLUP);
  pinMode(inputSwitch, INPUT_PULLUP);
  pinMode(modeSwitch, INPUT_PULLUP);
  pinMode(limitSwitch, INPUT_PULLUP);
  myPID.SetMode(AUTOMATIC);
  myPID.SetOutputLimits(-255, 255);
  myPID.SetSampleTime(20);
  // --- ホーミング ---
  while (digitalRead(limitSwitch) != 1) {
    digitalWrite(motor_IN1, LOW);
    analogWrite(motor_IN2, 70);
    encoderValue = as5600.readAngle();
  }
  while (digitalRead(limitSwitch) != 0) {
    analogWrite(motor_IN1, 50);
    digitalWrite(motor_IN2, LOW);
  }
  digitalWrite(motor_IN1, LOW);
  digitalWrite(motor_IN2, LOW);
  startPosition = as5600.readAngle() * 0.087890625;
  endPosition = 6000;
  Setpoint = startPosition;
  // --- ホーミング終了 ---
}

void loop() {
  // エンコーダー値読み取り - 現在位置
  rodPosition = as5600.readAngle() / 0.001953125; // mm単位 - リードスクリュー8mm/回転、エンコーダーRAW値4096/回転、8/4096でmm換算
  // シリアル通信モード
  if (digitalRead(modeSwitch) == 0) {
    while (Serial.available() > 0) {
      serialInput = Serial.readString();
      serialInput.trim();
      // "save"で現在位置を配列に保存
      if (serialInput == "save") {
        positionsArray[positionsCounter] = totalDistance;
        delay(1000);
        positionsCounter++;
      }
      // "clear"で保存位置クリア
      if (serialInput == "clear") {
        memset(positionsArray, 0, sizeof(positionsArray));
        positionsCounter = 0;
      }
      // 数値入力をセットポイントとして使用
      serialIntInput = serialInput.toInt();
      if (serialIntInput != 0) {
        if (serialIntInput < 0) serialIntInput = 0;
        if (serialIntInput > 150) serialIntInput = 150;
        Setpoint = serialIntInput * 45; // mmを度に変換(1mm直線 = 45度回転)
      }
    }
    // 保存位置の再生
    if (serialInput == "run") {
      while (serialInput != "stop") {
        for (int i = 0; i <= positionsCounter - 1; i++) {
          if (serialInput == "stop") break;
          while (positionsArray[i] > totalDistance + 25 || positionsArray[i] < totalDistance - 25 || pwmValue != 0) {
            Setpoint = positionsArray[i];
            readEncoder();
            runMotor();
          }
          delay(2000); // ステップ間遅延
          while (Serial.available() > 0) {
            serialInput = Serial.readString();
            serialInput.trim();
          }
        }
      }
    }
  }
  // ポテンショメーター/RC受信機制御モード
  else if (digitalRead(modeSwitch) == 1) {
    if (digitalRead(inputSwitch) == 0) {
      // アナログ入力 - ポテンショメーター
      desiredValue = analogRead(A0);
      if (desiredValue < 15) desiredValue = 15;
      if (desiredValue > 1008) desiredValue = 1008;
      Setpoint = map(desiredValue, 15, 1008, startPosition, endPosition);
    } else if (digitalRead(inputSwitch) == 1) {
      // デジタル入力 - RC送信機
      desiredValue = pulseIn(ch1, HIGH, 30000);
      if (desiredValue < 1000 || desiredValue > 2000) desiredValue = 1000;
      Setpoint = map(desiredValue, 1000, 2000, startPosition, endPosition);
    }
  }
  // セットポイントの最小/最大値制限
  if (Setpoint > endPosition) Setpoint = endPosition;
  if (Setpoint < startPosition) Setpoint = startPosition;
  // 感度調整(コメントアウト中)
  // Pk1 = analogRead(A2) * 0.002;
  // myPID.SetTunings(Pk1, Ik1, Dk1);
  readEncoder();
  runMotor();
  // "set"ボタンで開始/終了位置設定
  if (digitalRead(setButton) == LOW) {
    delay(3000);
    if (digitalRead(setButton) == LOW) {
      endPosition = totalDistance;
      while (digitalRead(setButton) != HIGH);
    } else {
      startPosition = totalDistance;
    }
  }
}

void readEncoder() {
  // エンコーダーRAW値を角度値に変換し、シャフト角度位置を追跡
  encoderValue = as5600.readAngle() * 0.087890625;
  // 第1象限
  if (encoderValue >= 0 && encoderValue <= 90) quadrantNumber = 1;
  // 第2象限
  if (encoderValue >= 90 && encoderValue <= 180) quadrantNumber = 2;
  // 第3象限
  if (encoderValue >= 180 && encoderValue <= 270) quadrantNumber = 3;
  // 第4象限
  if (encoderValue >= 270 && encoderValue <= 360) quadrantNumber = 4;
  if (quadrantNumber != previousQuadrantNumber) {
    // 第4→第1象限遷移
    if (quadrantNumber == 1 && previousQuadrantNumber == 4) numberOfTurns++;
    // 第1→第4象限遷移
    if (quadrantNumber == 4 && previousQuadrantNumber == 1) numberOfTurns--;
    previousQuadrantNumber = quadrantNumber;
  }
  if (totalDistance >= 0) {
    totalDistance = (numberOfTurns * 360) + encoderValue;
  } else {
    totalDistance = (numberOfTurns * 360) + encoderValue;
  }
  Input = totalDistance; // PID入力値 = 現在位置
}

void runMotor() {
  myPID.Compute(); // PID演算で出力値取得
  // 右回転(正方向)
  if (Output > 1) {
    pwmValue = Output;
    if (pwmValue < 50 && pwmValue > 15) pwmValue = pwmValue + 40;
    if (pwmValue <= 15) pwmValue = 0;
    analogWrite(motor_IN1, pwmValue);
    digitalWrite(motor_IN2, LOW);
  }
  // 左回転(負方向)
  else if (Output < 1) {
    pwmValue = abs(Output);
    if (pwmValue < 50 && pwmValue > 15) pwmValue = pwmValue + 40;
    if (pwmValue <= 15) pwmValue = 0;
    digitalWrite(motor_IN1, LOW);
    analogWrite(motor_IN2, pwmValue);
  }
  // 停止
  else if (Output > -1 && Output < 1) {
    pwmValue = 0;
    digitalWrite(motor_IN1, LOW);
    digitalWrite(motor_IN2, LOW);
  }
}

コード詳細

ループ冒頭でエンコーダー値(現在位置)を読み取り、mm単位に変換。シリアル通信モード(modeSwitch=LOW)ではシリアルモニター入力を監視:"save"で現在位置記録、"clear"で全消去、数値(0-150mm)をセットポイントとして設定(mm→度変換:×45、リードスクリューピッチ8mmの場合。ピッチ違えば変更必要)。"run"で記録位置を順次実行(while/forループ)。

ポテンショメーター/RCモード(modeSwitch=HIGH)では、inputSwitchでアナログ/デジタルを判別。アナログならポテンショメーター値をセットポイントにマッピング、デジタルならRC受信機のパルス幅を読み取りマッピング。その後readEncoder()とrunMotor()でPID制御実行。

readEncoder()ではRAW値を角度変換し、象限判定で回転数をカウント。totalDistanceをPIDのInput(現在値)とする。トリマーポテンショメーター(A2)でPIDのPゲイン調整可能(コメントアウト中)。runMotor()でPID出力に基づきPWM駆動(デッドバンド補正付き)。

PIDゲイン初期値:P=0.65, I=0, D=0.1。これらの値で応答特性が決まる。

テスト結果

精度テスト:1mm刻み移動で初回0.8mm誤差、以降4回はほぼ1mm、4mm移動で0.15mm誤差。リードスクリュー/ナット間に約0.25mmのバックラッシュ、3Dプリントギアとモーター内蔵ギアにもバックラッシュあり。負荷をかけると誤差増大だが、PIDチューニングで改善可能。

以上でこのチュートリアルを終了します。お楽しみいただけたら幸いです。

製造プロセス

  1. プロフェッショナル木材ステイン:歴史、種類、生産
  2. 信頼できるCNC加工パートナーの選び方
  3. フライスと旋削の統合:マルチタスキングマシンで効率向上
  4. 高圧ダイカスト徹底解説:プロセス、材料、用途
  5. 高度なCNC横形マシニングセンタ(HMC)による生産効率の最大化
  6. ESP8266・Arduino・超音波センサーで作るスマートIoTジャー:リアルタイム中身監視
  7. Arduino Nanoで作るDIYミニオシロスコープ – ステップバイステップチュートリアル
  8. 自律走行Donkey Car:TensorFlow MobileNetV2 AIでゴミ収集
  9. ArduinoベースのMIDIステッピングモーターシンセサイザー
  10. 精密CNC加工に不可欠な主要加工工具
  11. フライス加工用カッターの種類と選定ガイド