シリアル通信によるサーボモータ制御

Arduino に関するご質問などはこちらへ。

シリアル通信によるサーボモータ制御

投稿記事by Y_J » 2013年12月26日(木) 15:48

どうも、こんにちは。

現在私はArduinoを使って3関節の六足歩行ロボットを作成しています。
そこで、文字列を受信して、それによってサーボモータ(計18個)を動かすプログラムを試作しました。

動作を簡単に説明しますと、
①PC側から”[1,180,90,0]”といったような形式の文字列をシリアル通信によって受信。
②文字列を分割し、最初の文字を”ch”とし、それ以降の数値をそれぞれ”o[ch][1]”,”o[ch][2]”,”o[ch][3]”に格納。
③格納した数値を接続されたサーボモータへ出力。

プログラム↓
//------------------------------------------------------------------------------------
#include <string.h>
#include <Servo.h>
#define Wait (1000/200)
Servo servo[8][4];
//------------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
Serial.println( "OK" );

servo[1][1].attach(22);
servo[1][2].attach(24);
servo[1][3].attach(26);

servo[2][1].attach(28);
servo[2][2].attach(30);
servo[2][3].attach(32);

servo[3][1].attach(34);
servo[3][2].attach(36);
servo[3][3].attach(38);

servo[4][1].attach(40);
servo[4][2].attach(42);
servo[4][3].attach(44);

servo[5][1].attach(46);
servo[5][2].attach(48);
servo[5][3].attach(49);

servo[6][1].attach(47);
servo[6][2].attach(45);
servo[6][3].attach(43);
}
//------------------------------------------------------------------------------------
void loop() {
////////////////////////////////////////////////////////開始
int DataSize = Serial.available();
int i = 0;
int o[8][4];
char m[256];
char *tp;
String s = "";
////////////////////////////////////////////////////////受信
//送信されてきたした文字列を1つの文字列として受信する。
if( DataSize > 0 ){
m[0] = Serial.read();
if(m[0] == '['){
delay(Wait);
for(i=1 ; i < 256 ; i++){
if(Serial.peek() != -1){
m[i] = Serial.read();
if(m[i] == ']'){ break; }
}
else{ i-=1; delay(Wait); }
}
//Serial.print("OK:");
int mSize = i;
for(i=0 ; i <= mSize ; i++){ s.concat(m[i]); }
Serial.println(s);//受け取り確認
////////////////////////////////////////////////////////格納
//受信した文字列を分解し、各変数に格納する。
for(i=0 ; i < mSize ; i++){ m[i]=m[i+1]; }
m[i-1] = ',';
/*
for(i=0 ; i < (mSize) ; i++){
Serial.println( m[i] );
}
*/
tp = strtok( m, "," );
int ch = atoi(tp);
//Serial.print( tp ); Serial.print( '/' );
Serial.println( ch );//受け取り確認

for(i=1 ; i <= 3 ; i++){
tp = strtok( NULL, "," );
o[ch][i] = atoi(tp);
//Serial.print( tp ); Serial.print( '/' );
Serial.println( o[ch][i] );//受け取り確認
}
////////////////////////////////////////////////////////伝達
//格納した数値を駆動命令としてサーボモータへ伝達する。
//if(ch < 6 && o[ch][1] < 180 && o[ch][2] < 180 && o[ch][3] < 180){
servo[ch][1].write(o[ch][1]);
servo[ch][2].write(o[ch][2]);
servo[ch][3].write(o[ch][3]);
//}
//else{ Serial.println( "No" ); }
////////////////////////////////////////////////////////終了
}
}
else{}//データが存在しなかった場合
}
//------------------------------------------------------------------------------------

こんな感じです。

で、ここからが本題なのですが、このプログラムを使用してみたところ、以下のようなエラーが出てうまくいきません。

java.io.IOException: Input/output error in writeArray
at gnu.io.RXTXPort.writeArray(Native Method)
at gnu.io.RXTXPort$SerialOutputStream.write(RXTXPort.java:1124)
at processing.app.Serial.write(Serial.java:517)
at processing.app.Serial.write(Serial.java:540)
at processing.app.SerialMonitor.send(SerialMonitor.java:200)
at processing.app.SerialMonitor.access$100(SerialMonitor.java:32)
at processing.app.SerialMonitor$3.actionPerformed(SerialMonitor.java:89)
at javax.swing.JTextField.fireActionPerformed(JTextField.java:492)
at javax.swing.JTextField.postActionEvent(JTextField.java:705)
at javax.swing.JTextField$NotifyAction.actionPerformed(JTextField.java:820)
at javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1636)
at javax.swing.JComponent.processKeyBinding(JComponent.java:2851)
at javax.swing.JComponent.processKeyBindings(JComponent.java:2886)
at javax.swing.JComponent.processKeyEvent(JComponent.java:2814)
at java.awt.Component.processEvent(Component.java:6040)
at java.awt.Container.processEvent(Container.java:2041)
at java.awt.Component.dispatchEventImpl(Component.java:4630)
at java.awt.Container.dispatchEventImpl(Container.java:2099)
at java.awt.Component.dispatchEvent(Component.java:4460)
at java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1848)
at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:704)
at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:969)
at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:841)
at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:668)
at java.awt.Component.dispatchEventImpl(Component.java:4502)
at java.awt.Container.dispatchEventImpl(Container.java:2099)
at java.awt.Window.dispatchEventImpl(Window.java:2475)
at java.awt.Component.dispatchEvent(Component.java:4460)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

具体的にいうと、最初の数回はうまくいっても、その途中でサーボが動かなくなります。
それどころか、途中でシリアルポートの通信が切れてしまうようで、IED内でもCOMを認識してくれません。
そのため、Arduinoのリセットボタンを押しても通信は回復せず、USBケーブルをさしなおさなければなりません。
また、モータの方も1秒おき程度に指定していない角度へ動いていくなど、妙な挙動を引き起こします。

この問題に心当たりがあれば何卒返信をお願いいたします。
Y_J
 
記事: 22
登録日時: 2013年5月30日(木) 11:10

Re: シリアル通信によるサーボモータ制御

投稿記事by yamaguch » 2013年12月27日(金) 15:18

こんにちは、

コード: 全て選択
java.io.IOException: Input/output error in writeArray
at gnu.io.RXTXPort.writeArray(Native Method)
at gnu.io.RXTXPort$SerialOutputStream.write(RXTXPort.java:1124)
at processing.app.Serial.write(Serial.java:517)
at processing.app.Serial.write(Serial.java:540)
at processing.app.SerialMonitor.send(SerialMonitor.java:200)
at processing.app.SerialMonitor.access$100(SerialMonitor.java:32)
at processing.app.SerialMonitor$3.actionPerformed(SerialMonitor.java:89)
at javax.swing.JTextField.fireActionPerformed(JTextField.java:492)
at javax.swing.JTextField.postActionEvent(JTextField.java:705)
at javax.swing.JTextField$NotifyAction.actionPerformed(JTextField.java:820)

SerialMonitor の上の方ににテキストを入力する行がありますが、ここに文字列を入れてリターンキーを押した際に
シリアルポートに出力する writeArray() というメソッドの中でエラーになっていますね。

シリアルモニターから1行のデータを Arduino に送っていますが、この方法で PC と Arduino の間で思い通りのデータの受け渡しができていることは確認されていますか。

string.h ではなく Arduino の Serial.parseInt() や String を使う方がスケッチが簡単になるような気がします ;)

コード: 全て選択
#include <Servo.h>

Servo servo[6][3];

uint8_t channel[][3] = {
    {22, 24, 26}, {28, 30, 32}, {34, 36, 38},
    {40, 42, 44}, {46, 48, 49}, {47, 45, 43}
};

void setup() {
    Serial.begin(9600);
    Serial.println();
    for (int i = 0; i < 6; i++) {
        for (int j = 0; j < 3; j++) {
            servo[i][j].attach(channel[i][j]);
        }
    }
}

void loop() {
    if (Serial.available() > 0) {
        int ch = Serial.parseInt();
        int value[] = {Serial.parseInt(), Serial.parseInt(), Serial.parseInt()};
        Serial.readStringUntil('\n');
        for (int i = 0; i < 3; i++) {
            Serial.println(String("servo[") + ch + "][" + i + "].write(" + value[i] + ")" );
            //servo[ch][i].write(value[i]);
        }
    }
}

山口
yamaguch
 
記事: 482
登録日時: 2010年7月06日(火) 17:37

Re: シリアル通信によるサーボモータ制御

投稿記事by Y_J » 2014年1月07日(火) 17:20

かなりの間時間を空けてしまいまして申し訳ありません。
そしてあけましておめでとうございます。

かなりすっきりとしたプログラムになってしまいまして、ありがとうございます。
動作確認してみましたところ、別な問題が発生しましたので報告します。

本プログラムを利用してサーボモータを動作させたところ、1つだけの場合は問題なく動作するのですが、
2つ以上、そして可動域が大きい場合に、同様にシリアルポートが切断されてしまいました。

これはもしかして電流とかその辺の問題なのでしょうか?
もしそうである場合、電源を別からにするべきなのでしょうか?
Y_J
 
記事: 22
登録日時: 2013年5月30日(木) 11:10

Re: シリアル通信によるサーボモータ制御

投稿記事by yamaguch » 2014年1月07日(火) 17:43

こんにちは、

サーボの電源を Arduino の 5V から取っていると、うまく動かない可能性が高いと思います。
電源は別系統にする方がいいでしょう。

山口
yamaguch
 
記事: 482
登録日時: 2010年7月06日(火) 17:37

Re: シリアル通信によるサーボモータ制御

投稿記事by Y_J » 2014年1月08日(水) 15:22

電源を別にしてみたところ、無事動きました。

ありがとうございました。
Y_J
 
記事: 22
登録日時: 2013年5月30日(木) 11:10


Return to Arduino 質問箱

cron