2軸Joystick モジュールをPico につないで、2軸のカメラマウントを動かしてみます。
開発環境はArduino IDE とMicroPython の2つを使ってみます。
Arduino IDE
MicroPython
joystick の操作
組み立ててみました
動かしてみました
構成
ラズパイ4 Model B はPico にファームウェアやプログラムを仕込むのに使います。
実際にカメラを使う場合は、processingなどでカメラ映像を表示するのに使います。
参照 Pi Pico でエッジAI を試してみる(1)カメラ
用意するもの
ラズパイ4 Model B
Pico W(H)
Joystickモジュール
カメラマウント
Servo motor(sg-90を使ってみる)
GPIO について
Pico ではADCが5つ使えて、そのうち3つ(GP26,27,28)がユーザーに開放されています。今回はそれを使います(VRX、VRY、SW)。
サーボモータには、GP0とGP4を使います。
Wiring (配線)
TOP
Arduino IDE
母艦(ラズパイ4)にArduino IDE をインストールしておきます。
ラズパイ4でArduino IDE を使ってみる
ボードマネージャーにEarle Philhower版をインストール」しておきます。
Arduino IDE でPi Pico Wの開発用にEarle Philhower版を使う
ツールー>ボードー>ボードマネージャー>Raspberry Pi RP2040 Board(3.3.2)ー>Raspberry Pi Pico W
ファイルー>新規ファイルで開いて書き込みます。
コードはこんな感じです。
Map関数の最大値、最小値はloop内のX_POS , Y_POS をシリアルモニターで確認しておいてください(表示が速すぎる場合はdelay を1000くらいにする)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include <Servo.h>
Servo myservo_A ; // create servo object to control a servo
Servo myservo_B ;
int pos_A = 0 ;
int pos_B = 0 ;
int x_key = 26 ; // initializing ADC0 for storing the joystick’s x key value
int y_key = 27 ; // initializing ADC1 for storing the joystick’s y key value
int Push_Button = 28 ; // initializing digital pin 28 for joystick’s press to select button
int x_pos = 0 ; // Declaring a variable for storing the value
int y_pos = 0 ; // Declaring a variable for storing the value
int btn_state = 0 ; // Declaring a variable for storing the value
String js_state = "" ;
String prev_state = "" ;
void setup ( ) {
pinMode ( LED_BUILTIN , OUTPUT ) ;
Serial . begin ( 115200 ) ; // initializing serial communications at 115200 bps:
pinMode ( x_key , INPUT ) ; // Selecting Arduino analog ADC0 pin as input
pinMode ( y_key , INPUT ) ; // Selecting Arduino analog ADC1 pin as input
pinMode ( Push_Button , INPUT _ PULLUP ) ; // This will activate pull-up resistor on the push-button pin
myservo_A . attach ( 0 , 544 , 2400 ) ;
myservo_B . attach ( 2 , 544 , 2400 ) ;
}
int Map ( int x , int in_min , int in_max , int out_min , int out_max ) {
return ( x - in_min ) * ( out_max - out_min ) / ( in_max - in_min ) + out_min ;
}
void ctrl_servo_A ( String s ) {
if ( s == "up" ) {
pos_A ++ ;
} else if ( s == "down" ) {
pos_A -- ;
}
if ( pos_A > 180 ) {
pos_A = 180 ;
}
if ( pos_A < 0 ) {
pos_A = 0 ;
}
myservo_A . write ( pos_A ) ;
}
void ctrl_servo_B ( String s ) {
if ( s == "left" ) {
pos_B ++ ;
} else if ( s == "right" ) {
pos_B -- ;
}
if ( pos_B > 180 ) {
pos_B = 180 ;
}
if ( pos_B < 0 ) {
pos_B = 0 ;
}
myservo_B . write ( pos_B ) ;
}
void loop ( ) {
digitalWrite ( LED_BUILTIN , HIGH ) ;
x_pos = analogRead ( x_key ) ; // Reading the value from ADC0 and storing in x_pos
y_pos = analogRead ( y_key ) ; // Reading the value from ADC1 and storing in y_pos
btn_state = digitalRead ( Push_Button ) ; // Reading the digital pin 28 is high or low
Serial . print ( "X_POS = " ) ;
Serial . println ( x_pos ) ;
Serial . print ( "Y_POS = " ) ;
Serial . println ( y_pos ) ;
//X_POS , Y_POS の最大値、最小値をセット
int adc_x = round ( Map ( x_pos , 0 , 4095 , 0 , 18 ) ) ;
int adc_y = round ( Map ( y_pos , 0 , 4095 , 0 , 18 ) ) ;
String js_state = "HOME" ;
if ( btn_state == 1 ) {
if ( ( adc_x == 0 ) && ( adc_y == 13 ) ) {
js_state = "UP" ;
}
if ( ( adc_x == 18 ) && ( adc_y == 18 ) ) {
js_state = "DOWN" ;
}
if ( ( adc_x == 1 ) && ( adc_y == 18 ) ) {
js_state = "LEFT" ;
}
if ( ( adc_x == 0 ) && ( adc_y == 0 ) ) {
js_state = "RIGHT" ;
}
} else if ( btn_state == 0 ) {
js_state = "PRESS" ;
}
if ( js_state == "UP" ) {
ctrl_servo_A ( "up" ) ;
} else if ( js_state == "DOWN" ) {
ctrl_servo_A ( "down" ) ;
}
if ( js_state == "LEFT" ) {
ctrl_servo_B ( "left" ) ;
} else if ( js_state == "RIGHT" ) {
ctrl_servo_B ( "right" ) ;
}
if ( js_state == "PRESS" ) {
myservo_A . write ( 90 ) ;
myservo_B . write ( 90 ) ;
}
delay ( 10 ) ; // This will add delay between readings
}
Pico をラズパイに接続して、マイコンボードに書き込むをクリック
TOP
MicroPython
Pico W にMicropython用ファームウェアUF2をインストール
UF2 ファイルは、Raspberry Pi MicroPython ドキュメント ページ にあります。
どれでもいいと思います。
ファイルをダウンロードしたら、BOOTSEL ボタンを押しながら、USB ケーブルを使用して Raspberry Pi Pico を母艦に接続します。 RPI-RP2 という名前の新しいドライブがファイル エクスプローラーに表示されます。 UF2 ファイルをこのドライブにドラッグ アンド ドロップ します。 完了すると、ドライブはファイル エクスプローラーから自動的に消えます。
Thonny IDE を使う
母艦であるラズパイにはThonny はプレインストールされています。
起動して新しいプロジェクトを開き(NEW)、以下の2つのコードをPico W に書き込み、main.pyを実行します(RUN)。
【servo.py】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
from machine import Pin , PWM
class Servo :
"" " A simple class for controlling a 9g servo with the Raspberry Pi Pico.
Attributes:
minVal: An integer denoting the minimum duty value for the servo motor.
maxVal: An integer denoting the maximum duty value for the servo motor.
" ""
def __init__ ( self , pin : int or Pin or PWM , minVal = 2500 , maxVal = 7500 ) :
"" " Creates a new Servo Object.
args:
pin (int or machine.Pin or machine.PWM): Either an integer denoting the number of the GPIO pin or an already constructed Pin or PWM object that is connected to the servo.
minVal (int): Optional, denotes the minimum duty value to be used for this servo.
maxVal (int): Optional, denotes the maximum duty value to be used for this servo.
" ""
if isinstance ( pin , int ) :
pin = Pin ( pin , Pin . OUT )
if isinstance ( pin , Pin ) :
self . __pwm = PWM ( pin )
if isinstance ( pin , PWM ) :
self . __pwm = pin
self . __pwm . freq ( 50 )
self . minVal = minVal
self . maxVal = maxVal
def deinit ( self ) :
"" " Deinitializes the underlying PWM object.
" ""
self . __pwm . deinit ( )
def goto ( self , value : int ) :
"" " Moves the servo to the specified position.
args:
value (int): The position to move to, represented by a value from 0 to 1024 (inclusive).
" ""
if value < 0 :
value = 0
if value > 1024 :
value = 1024
delta = self . maxVal - self . minVal
target = int ( self . minVal + ( ( value / 1024 ) * delta ) )
self . __pwm . duty_u16 ( target )
def middle ( self ) :
"" " Moves the servo to the middle.
" ""
self . goto ( 512 )
def free ( self ) :
"" " Allows the servo to be moved freely.
" ""
self . __pwm . duty_u16 ( 0 )
【main.py】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import machine
from machine import Pin , ADC
from time import sleep
from servo import Servo
led = machine . Pin ( "LED" , machine . Pin . OUT )
s1 = Servo ( 0 )
s2 = Servo ( 2 )
VRX = ADC ( 0 ) # ADC0 multiplexing pin is GP26
VRY = ADC ( 1 ) # ADC1 multiplexing pin is GP27
SW = Pin ( 28 , Pin . IN , Pin . PULL_UP ) # ADC2 multiplexing pin is GP28
def Map ( x , in_min , in_max , out_min , out_max ) :
return ( x - in_min ) * ( out_max - out_min ) / ( in_max - in_min ) + out_min
def servo_Angle ( device , angle ) :
if ( device == "A" ) :
s1 . goto ( round ( Map ( angle , 0 , 180 , 0 , 1024 ) ) )
elif ( device == "B" ) :
s2 . goto ( round ( Map ( angle , 0 , 180 , 0 , 1024 ) ) )
elif ( device == "AB" ) :
s1 . goto ( round ( Map ( angle , 0 , 180 , 0 , 1024 ) ) )
s2 . goto ( round ( Map ( angle , 0 , 180 , 0 , 1024 ) ) )
def direction ( ) :
global i
i = 0
adc_X = round ( Map ( VRX . read_u16 ( ) , 0 , 65535 , 0 , 255 ) )
adc_Y = round ( Map ( VRY . read_u16 ( ) , 0 , 65535 , 0 , 255 ) )
Switch = SW . value ( )
if adc_X <= 30 :
print ( "UP" )
i = 1 # Define up direction
elif adc_X >= 255 :
print ( "DOWN" )
i = 2 # Define down direction
elif adc_Y >= 255 :
print ( "LEFT" )
i = 3 # Define left direction
elif adc_Y <= 30 :
print ( "RIGHT" )
i = 4 # Define right direction
elif Switch == 0 : #and adc_Y ==128:
print ( "PRESS" )
i = 5 # Define Button pressed
elif adc_X - 125 < 15 and adc_X - 125 > - 15 and adc_Y - 125 < 15 and adc_Y - 125 > - 15 and Switch == 1 :
print ( "HOME" )
i = 0 # Define home location
def loop ( ) :
num = 90
device = ""
while True :
led . value ( 1 )
direction ( ) # Call the direction judgment function
#servo_Angle(num)
sleep ( 0.01 )
#up
if i == 1 :
#num = 0
num = num - 1
if num < 0 :
num = 0
device = "A"
#down
if i == 2 :
#num = 180
num = num + 1
if num > 180 :
num = 180
device = "A"
#left
if i == 3 :
num = num - 1
if num < 0 :
num = 0
device = "B"
#right
if i == 4 :
num = num + 1
if num > 180 :
num = 180
device = "B"
#
if i == 5 :
num = 90
device = "AB"
print ( device )
servo_Angle ( device , num )
if __name__ == '__main__' :
loop ( ) # call the loop function
TOP
サーボモーターを動かしてみましょう。
Joystick の動き(よく見るとちょっと変な気もしますが……..気にしない)
サーボモータの回転は以下のように制御されます。
ホームポジションをこう取った場合
PRESS
グッと押し込むとGNDに接続されて、ホーンはhomeポジションに戻ります。
UP
ホーンは以下の位置まで瞬時に回転します。
DOWN
ホーンは以下の位置まで瞬時に回転します。
LEFT
ホーンは以下の位置までゆっくり回転します。速度はloop内のsleep()の引数で調整できます(0.01の値を大きくすればよりゆっくり回転します)。
RIGHT
ホーンは以下の位置までゆっくり回転します。速度はloop内のsleep()の引数で調整できます(0.01の値を大きくすればよりゆっくり回転します)。
TOP
カメラマウントを組み立ててみました
部品はこれだけです。
A に上部モーターがセットされ、B に下部モーターがセットされます。C がそれをつなぐアームです。
全体像
下部のモーターをセットして、上部のモーターを支えるアームを組みます。こんな感じです。
なかなかキツいですが、何とか入るサイズになっています。
十字のホーンを切って削って押し込み、ここにモータをセットします。
上部のモーターをセットする場合、事前に0度で回転させておいて、そのレベルでモーターを軸受けにセットします。0度の位置で上のモーターが下のモータに接触しないように調整しましょう。
上部モーターのホームポジション (90 度の位置)
この時点で天井板をセットします。ただし、この製品、精度に問題があり、あまりきつくねじ止めすると、モーターの軸と軸受けがズレてしまい、回転時にズレのため軸に異常に負荷がかかり過電流が流れて発熱してモーターが死んでしまったことがあります。
ねじ止めも隙間を空けてゆるく止めておきます。
このsg-90も含めて低価格のサーボモーターはセーフティーがほぼ無いので、回転時に負荷がかかるのは禁止です。
TOP
動かしてみました。
ちなみに、動画の奥に見えているのがPico です。
VIDEO
最後にJoystickをpressして両方のモーターをホームポジションに戻しています。
TOP
Leave a Reply