Raspberry PiとArduinoを連携させる

Raspberry PiとArduinoを連携させます。
Raspberry PiからArduinoにサーボモーターの角度を指示します。

platformio

Raspberry PiからArduinoにプログラムをダウンロードできるようにします。
platformioを使います。

platformioをインストールします。


> sudo pip install platformio

platformioを使ってArduinoにプログラムを転送するまでの手順です。


> mkdir tiny_project_x
> cd tiny_project_x
> platformio init --board uno

platformio.iniファイルと、srcディレクトリと、libディレクトリが生成されます。srcディレクトリにソースコードを配置して、コンパイル、転送します。


> vi src/xxx.ino
> platformio run
> platformio run --target upload

Arduino unoを使う場合、下記だけ覚えておけば、Arduinoを操作できます。

platformio boards ボードの種類一覧を表示する
platformio init --board uno ボードをunoに設定する
platformio run プログラムをコンパイルする
platformio run --target upload プログラムをボードに転送する

Raspberry PiとArduinoを接続する

Raspberry_Pi_Arduino_001

Raspberry Piは、USB Standard Aで、Arduinoは、USB Standard Bになります。USBケーブルを買う必要があるかもしれません。

プログラムで実現することを決める

Raspberry Piからシリアル通信を使ってArduinoを制御します。
Raspberry Piは、サーボモーターの角度をArduinoに指示します。

プログラムを設計する

角度の数値を表す文字列を送信して、xが続けば横方向の回転を表し、yが続けば縦方向の回転を表す、とします。

[Raspberry Pi] -> “0x” or “10x” or “180x” -> [Arduino] -> [サーボモーター]
[Raspberry Pi] -> “0y” or “10y” or “180y” -> [Arduino] -> [サーボモーター]

角度を送信してから、どちらのサーボモーターを動かすかの指示を送る方が、逆の順に送信するよりプログラムが簡単になります。
このカメラマウントは、下側のサーボモーターが横方向に回転して、上側のサーボモーターが縦方向に回転します。

ボーレートを9600として十分に速く動作するため、1Byteまたは2Byteの送信で実現できる処理ですが、最大4Byteの送信で実現します。

プログラムを実装する

まず、Arduino側のソースコードを掲載します。MeanShiftのときにも、このソースコードを使います。
ArduinoのStringクラスと、C++のSTLのStringクラスが違うもので、C++のSTLをArduinoに持ってくるのも一手間必要とのことだったので、C言語の書き方で作成しています。
strtolは、string to longを意味します。C言語では、お決まりの略語がかなりの数あります。文字の配列を数値に変換するための関数です。マイナス(の値)や16進数が入力されることが絶対にない前提の関数としています。
‘q’を受け取ったとき、無限ループで何もしない処理に入れます。Arduinoがloop関数を繰り返すことに対応した処理になります。これは後ほど使います。

servo_motor.ino

// delay( 1000 ) = 1 second

// --------------           ---------
// |            |           |       |<------->|Servomotor|
// |Raspberry Pi|<--------->|Arduino|
// |            |           |       |<------->|Servomotor|
// --------------           ---------

// -----------------------------------------------------------------------------

#include <Servo.h>

const int LOWER_MOTOR_PIN = 10;
const int UPPER_MOTOR_PIN = 11;

const int DELAY_TIME = 10;

Servo lower_motor;
Servo upper_motor;

byte usb_serial_value;

// -----------------------------------------------------------------------------

void
setup()
{
    lower_motor.attach( LOWER_MOTOR_PIN );
    upper_motor.attach( UPPER_MOTOR_PIN );

    Serial.begin( 9600 );
    // Serial.begin( 115200 );

    /// Raspberry Pi send "~." -> connection is deleted
    while ( !Serial ) {}
}

// -----------------------------------------------------------------------------

void
loop()
{
    int x_value = 90;
    int y_value = 90;
    char value_string[4];
    int index = 0;

    for (;;)
    {
        if ( Serial.available() )
        {
            /// Raspberry Pi -> Arduino
            usb_serial_value = Serial.read();
    
            if ( isDigit(usb_serial_value) )
            {
                /// get value
                value_string[index] = usb_serial_value;
                ++index;
            }
            else if ( usb_serial_value == 'x' )
            {
                value_string[index] = '\0';
                index = 0;

                x_value = strtol(value_string);
                if ( 0 <= x_value && x_value <= 180 )
                {
                    lower_motor.write( x_value );
                    delay( DELAY_TIME );
                }
            }
            else if ( usb_serial_value == 'y' )
            {
                value_string[index] = '\0';
                index = 0;

                y_value = strtol(value_string);
                if ( 0 <= y_value && y_value <= 180 )
                {
                    upper_motor.write( y_value );
                    delay( DELAY_TIME );
                }
            }
            else if ( usb_serial_value == 'q' )
            {
                for (;;)
                {
                    delay( 1000 );
                }
            }
        }
    }
}

// -----------------------------------------------------------------------------

int
strtol( const char *s )
{
    int r = 0;
    while (*s) {
        r *= 10;
        r += *s - '0';
        ++s;
    }
    return r;
}

次に、Raspberry Pi側のソースコードを掲載します。こちらはテスト用のプログラムです。
USBでArduinoと接続してから2秒待つ必要があります。
縦方向90度で正面を向きます。(サーボモーターを取り付けるときの角度次第ですが。)
横方向について、60-120度で動作させます。
縦方向について、70-110度で動作させます。
カメラがサーボモーターに対して大きくて重いので、問題が起きないように範囲を狭めて動作させます。

test_servo_motor.py

# [USAGE]
#
# python test_servo_motor.py

# ------------------------------------------------------------------------------

import time
import serial

# ------------------------------------------------------------------------------

SLEEP_TIME = 0.1

# ------------------------------------------------------------------------------

## usb connect -> wait arduino boot

S = serial.Serial('/dev/ttyACM0', 9600)
# S = serial.Serial('/dev/ttyACM1', 9600) ## rarely
time.sleep(2)

# ------------------------------------------------------------------------------

## test x ( x : lower servo motor )

for move_degree_x in range(90, 120):
    x_str = '%dx' % (move_degree_x)
    print(x_str)
    S.write(x_str.encode('utf-8'))
    # print(S.readline().decode('utf-8'))
    time.sleep(SLEEP_TIME)

for move_degree_x in reversed(range(60, 120)):
    x_str = '%dx' % (move_degree_x)
    print(x_str)
    S.write(x_str.encode('utf-8'))
    # print(S.readline().decode('utf-8'))
    time.sleep(SLEEP_TIME)

for move_degree_x in range(60, 90):
    x_str = '%dx' % (move_degree_x)
    print(x_str)
    S.write(x_str.encode('utf-8'))
    # print(S.readline().decode('utf-8'))
    time.sleep(SLEEP_TIME)

# ------------------------------------------------------------------------------

## test y ( y : upper servo motor )

for move_degree_y in range(90, 110):
    y_str = '%dy' % (move_degree_y)
    print(y_str)
    S.write(y_str.encode('utf-8'))
    # print(S.readline().decode('utf-8'))
    time.sleep(SLEEP_TIME)

for move_degree_y in reversed(range(70, 110)):
    y_str = '%dy' % (move_degree_y)
    print(y_str)
    S.write(y_str.encode('utf-8'))
    # print(S.readline().decode('utf-8'))
    time.sleep(SLEEP_TIME)

for move_degree_y in range(70, 90):
    y_str = '%dy' % (move_degree_y)
    print(y_str)
    S.write(y_str.encode('utf-8'))
    # print(S.readline().decode('utf-8'))
    time.sleep(SLEEP_TIME)

# ------------------------------------------------------------------------------

S.close()

動作確認する

カメラマウントを組み立てた後の動画になります。

カメラマウントを組み立てる
で組み立てた場合のサーボモーターの向きでは、横方向について0度が右、180度が左になり、縦方向について0度が上、180度が下になります。
小刻みに震えているのは、カメラが重たいことと各部品をネジ留めしていないためと思われます。





«       »