位置フィードバック付き高精度リニアサーボアクチュエーターの自作ガイド
概要
このチュートリアルでは、リニアサーボアクチュエーターの自作方法を学びます。通常のリニアアクチュエーターは電圧印加で一方向へ動くだけですが、この自作版はフィードバックループシステムを備え、正確で再現性のある動きを簡単に制御できます。
「サーボアクチュエーター」と呼ばれる所以は、出力動作を精密に制御するフィードバックループを持つからです。
入力モード
アナログ入力: 線形ポテンショメーター、一般的なロータリーポテンショメーター、ジョイスティック(これもロータリータイプ)など、あらゆるタイプのポテンショメーターが使用可能。
デジタル入力: RC送信機で制御可能。RC受信機をアクチュエーターの入力として接続。
両モードとも接続はわずか3線(電源2線+信号1線)で完結します。
主な機能
- 出力ロッドのカスタム開始/終了位置設定
- 感度(応答速度)調整
- PC/ノートPCからシリアル通信で制御可能:Arduino IDEシリアルモニターからミリメートル単位で位置指定
- 動作の記録・再生:各位置で「save」と入力して位置を保存、「run」でループ再生
動作原理
クローズドループ制御は、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高速プリント、高品質を実現するコスパ最強機種。詳細レビューもご参照ください。
組み立て
部品リスト
機械部品
- リードスクリュー 8mm Tr8×8 …………………………… Amazon / AliExpress
- ボールベアリング 8×22×7mm ………………………… Amazon / AliExpress
- ボールベアリング 4×9×4mm ×4 ……………………… Amazon / AliExpress
- ボールベアリング 6×13×5mm 686-2RS ×1 ………… Amazon / AliExpress
- M3×5mm / M4×5mm ネジインサート ………………… Amazon / AliExpress
- M3/M4ボルト・ナット ………………………………… Amazon / AliExpress
ボルト: M3×8 - 10個; M3×10 - 2個; M3×10皿 - 1個; M4×6 - 2個; M4×25 - 2個; M4×30 - 2個; M2×8 - 6個; M4×5グリブネジ - 5個
電子部品
- AS5600磁気エンコーダー …………………………… Amazon / AliExpress
- DRV8871 DCモータードライバー …………………… Amazon / AliExpress
- ATmega328p-AU ………………………………………… Amazon / AliExpress
- 16MHz水晶発振子 ……………………………………… Amazon / AliExpress
- AMS1117 5Vレギュレーター ………………………… Amazon / AliExpress
- 3386P角型トリマーポテンショメーター …………… Amazon / AliExpress
- 0805コンデンサーキット ……………………………… Amazon / AliExpress
- 12V DCモーター ~50RPM …………………………… Amazon / AliExpress
注: アフィリエイトリンクを含みます。
リードスクリュー機構の組み立て
- シリンダーベースブロックに3Dプリントナット(リードスクリューと同ネジ)をねじ込む。タイトフィットで保持力確保。ナットにはグリブネジ用スレッドインサート穴あり。
- リードスクリューをベースブロックの2個のベアリング(外径22mm)で保持。
- 後部に駆動ギアを取り付け(ナット同様にネジ込み、グリブネジ固定)。モータートルクを伝達する重要な接続。
- ギアとナットにスレッドインサートを入れ、ベースブロックにもインサート設置。ギアとナットを逆方向にねじ込み(ベアリングへのアキシアル荷重を避けるため締めすぎ注意)、グリブネジで固定。
- プレートをベースブロックに取り付けベアリングを保持。リードスクリューサブアセンブリ完成。
- ロッド準備:中空ロッドにスレッドインサート設置。先端にガイドベアリング(外径13mm、内径6mm)を3Dプリント6mm中空シャフト経由でM3皿ネジ10mmで固定。
- シリンダーをベースブロックに取り付け。事前にマイクロリミットスイッチを設置(NC接続でハンダ付け、配線約15cm、シリンダー上部の穴を通しM2×8mmボルト2本で固定。ガイドベアリングが適切なタイミングで作動する専用モデル推奨)。
- スレッドインサート経由でM4×25mmボルト2本(上側のみ)でシリンダー固定。下側2本は後でギアボックス/PCBカバーと共用。
- リードスクリューにロッドをねじ込み、ガイドベアリングをシリンダーガイドレール間に挿入。後部ギアを回すとロッドが後退しリミットスイッチまで移動。
- シリンダーキャップ取り付け:外径9mmベアリング4個(3Dプリントシャフト用)を収容。シャフト挿入部は薄く破損しやすいので慎重に。ベアリングでロッドを支持・ガイド。M4ボルト4本で固定。
DCモーター取り付け
- DCモーターをM3ボルト6本で固定。
- モーターシャフトにギア取り付け、スレッドインサートとグリブネジで固定。
- ギア噛み合い確認後、ギアボックス/PCBカバーを後部に取り付け。ベースブロックに追加スレッドインサート設置。
- DCモーター配線をハンダ付け(約20cm)。ベースブロックの穴を通し、カバーのホルダー2つでギアから離して配線。
- カバー固定:下側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チューニングで改善可能。
以上でこのチュートリアルを終了します。お楽しみいただけたら幸いです。
製造プロセス
- プロフェッショナル木材ステイン:歴史、種類、生産
- 信頼できるCNC加工パートナーの選び方
- フライスと旋削の統合:マルチタスキングマシンで効率向上
- 高圧ダイカスト徹底解説:プロセス、材料、用途
- 高度なCNC横形マシニングセンタ(HMC)による生産効率の最大化
- ESP8266・Arduino・超音波センサーで作るスマートIoTジャー:リアルタイム中身監視
- Arduino Nanoで作るDIYミニオシロスコープ – ステップバイステップチュートリアル
- 自律走行Donkey Car:TensorFlow MobileNetV2 AIでゴミ収集
- ArduinoベースのMIDIステッピングモーターシンセサイザー
- 精密CNC加工に不可欠な主要加工工具
- フライス加工用カッターの種類と選定ガイド