Lesson1 プチロボをC言語で動かす



1-1 Dev-Cppをセットアップする

ここでは、初心者のためにDev-CppのIDEを用いずにコマンドラインでコンパイル実行させます。従って、環境変数のPATHにC:\Dev-Cpp\binを登録してください。(推奨機の場合は入っているはず)

1-2 サーボモーターを動かしてみる

// servo.c
// プチロボットのサーボモーターのテストプログラム 
// g++ でコンパイルする

#include <stdio.h>
#include <ctype.h>
#include "comm.h" 
#define COMPORT "COM5"

int main()
{
    int key, end=1;
    int position=110;
    int i, save;

    printf("プチロボットテストプログラム\n+:10 -:-10 space:1 other:-1 q:終了\n");
    if(initServo(COMPORT)) return -1;
    while(end) {
        if(kbhit()) {
            key=readkey();
            save = position;
            switch(key&0x7f){
                case '+': 
                case ';': position+=10; break;
                case '-': position-=10; break;
                case 'q': end=0; break;
                case ' ': position++; break;
                default:  position--;
            }
            if((position <1) || (220<position)) position = save;
            else {
                printf("%d\n", position);
                fflush(stdout);
            }
        }
        for(i=0; i<9; i++)
            servo(i, position);
    }
    closeServo();
}
コンパイル方法
g++ servo.c servokey.o -o servo

これをコンパイルするには、関連するヘッダーファイルとオブジェクトファイルservokey.oが必要になります。ここをクリックしてダウンロードして下さい。これらはソースファイルと同一のディレクトリにある必要があります。
このservokey.oはC++で作られているのでg++というC++のコンパイラでコンパイルします。

このプログラムは+-キーなどを押すと9個のサーボモーターが一斉に同じように動くプログラムで、通信系、モーターのチェック用に使うこともできます。

1-3 プチロボの動きを作る

キャプチャーしながら頑張る。

1-4 msfファイルを読む

1-4-1 msfファイルについて

動きパターンの基本動作をプチロボのソフトで作り、保存します。例えば、まっすぐ歩くという動作の場合、右足を出して右足を後ろに蹴ると共に左足を前に出して・・という一連の動きを作ります。この過程をwalkという名前でキャプチャーし、ファイルに保存するとC:\Program Files\Puchi_Roboにwalk.msfというファイル名で保存されます。下の内容は単なるフォーマットの例なので歩きません。
;walk
0+22,1-11,2+9,3-46,4+40,5+17,6+0,7+22,8+14
0+35,1+9,2+29,3-56,4+30,5+7,6-16,7+22,8+14
0+25,1-1,2+19,3-66,4+20,5-3,6-26,7+22,8+14
0+15,1-11,2+9,3-76,4+10,5-13,6-26,7+12,8+4
0+15,1-11,2+9,3-76,4+10,5-13,6-26,7-8,8-16
0+15,1-11,2+9,3-76,4+10,5-13,6-26,7-28,8-36
0+15,1-11,2+9,3-86,4+0,5-23,6-26,7-28,8-36
;walkはコメントでファイル名と一致します。

次の行からが実際に動かすパターンで、一つ一つのサーボモーターに送るデータがカンマで区切られています。一番頭のデータは0+22ですが、データの頭の数字0がサーボモーターの番号です。その次の+22はホームポジションから見て相対的に+方向に22の位置に移動しろという命令です。後は同じようにして他のサーボモータの移動命令になります
 ここで問題になるのは、どこがホームポジションか分からないことです。一番初めにホームポジションに移動させなければ全てが始まりません。この情報は、先ほどのwalk.msfのあったフォルダと同じ所にあるPuchi_Robo_Config.iniにあります。 このファイルの中は例えば以下のようになっています。
[Comm]
Port=1
[Direction]
M0=+
M1=+
M2=+
M3=+
M4=+
M5=+
M6=+
M7=+
M8=+
[HomePosition]
M0=110
M1=110
M2=110
M3=110
M4=110
M5=110
M6=110
M7=110
M8=110
[Function]
Button3=walk
[FunctionPath]
Button3=C:\Program Files\Puchi_Robo\walk.msf
[HomePosition]から下のM0からM8までの値が各モーターのホームポジションの値です。全部110になっていますが、実際には色々な値になるはずです。可動範囲は大体1~220なので、この場合は全モーターが中心からスタートを意味します。

[Comm]は通信を行うポート番号です。

[Direction]はホームポジションからどちら周りを+と考えるかという設定です。+にすると時計周りとなります。

[Function]の定義のところには上で示したwalk.msfがボタン3に登録されていることを意味しますが、Cでプログラミングする場合には関係ありません。

1-4-2 初期設定ファイルを読んでサーボモーターをホームポジションに移動させる

/*
    read.c
    設定ファイルPuchi_Robo_Config.iniを読み込み、COMポート番号、
    ホームポジジョン、回転方向を配列に入れる。
    サーボモーターをホームポジションに移動させる。
*/
#include <stdio.h>
#include "comm.h" 

#define MotorNum 9
#define TRUE  1
#define FALSE 0

#define DEBUG

void initHome();
void initialization();

int direction[MotorNum];        // 回転方向
int home[MotorNum];             // ホームポジション
char COMport[10];
int now[MotorNum];              // モーターの現在位置
int port;

main()
{
    int i;

    initialization();
    sprintf(COMport, "COM%d", port);
#ifdef DEBUG
    printf("ComPort:%s\n", COMport);
#endif
    if(initServo(COMport)) return -1;
    // ホームポジションに移動
    initHome();
    closeServo();
}

void initialization()
{
    FILE *fp;
    char config[] = "Puchi_Robo_Config.ini";
    char buffer[100], *p;
    int i, j, position;
  
    fp = fopen(config, "r");
    while(fscanf(fp,"%s",buffer) >0) {
        if(buffer[0] !='[') continue;
        p = strchr(buffer,']');
        *p = 0;
        if(!strcmp(&buffer[1], "Comm")) {
            fscanf(fp,"%s",buffer);
            p = strchr(buffer,'=');
            *p++ = 0;
            port = atoi(p);
#ifdef DEBUG
            printf("Comm %d\n", port);
#endif
        }
        else if(!strcmp(&buffer[1], "HomePosition")) {
            for(i=0; i<MotorNum; i++) {
                fscanf(fp,"%s",buffer);
                if(buffer[0]=='M') {
                    p = strchr(buffer,'=');
                    *p++ = 0;
                    j = atoi(&buffer[1]);
                    if(j<MotorNum) 
                        home[j] = atoi(p);
                }
#ifdef DEBUG
                printf("HomePosition %s %d %d\n", buffer, j, home[j]);
#endif
            }
        }
        else if(!strcmp(&buffer[1], "Direction")) {
            for(i=0; i<MotorNum; i++) {
                fscanf(fp,"%s",buffer);
                if(buffer[0]=='M') {
                    p = strchr(buffer,'=');
                    *p++ = 0;
                    j = atoi(&buffer[1]);
                    if(j<MotorNum) 
                        direction[j] = (*p == '+') ? 1: -1;
                }
#ifdef DEBUG
                printf("Direction %s %d %d\n", buffer, j, direction[j]);
#endif
            }
        }
    }
    fclose(fp);
}

void initHome()
{
    int i, position;

    // ホームポジションに設定する
    for(i=0; i<MotorNum; i++) {
        position = (direction[i]==1) ? home[i] : 110 - (home[i] - 110);
        servo(i, position);
        now[i] = position;      // 現在位置
    }
}

課題

例えば下記のようなforward.msfを読み込んで、これを解析し実行する関数translationを作り、mainからこれを呼びロボットを動かしなさい。このファイル名はtrans.cとします。

;forward
S30
5+8,2+8
W300
1+10,0-25,4-10,3+15,8+40,7-40
W300
2-8,5-8
W300
1-10,0+25,4+10,3-15,8-40,7+73
W300
END

1-4-3 msfファイルを読んでみる

以下のソースをtrans1.cというファイル名で作成します。
msfファイルを読んでみる
#include <stdio.h>
#include <string.h>

#define MotorNum 9

void translation(char* filename);

main()
{
    translation("forward.msf");
}
// ファイルを読み込んで解釈実行 void translation(char* filename) { FILE *fp; char buffer[100]; fp = fopen(filename, "r"); while(fscanf(fp,"%s",buffer) >0) { if(!strcmp(buffer,"END")) break; // 終了 // if(buffer[0]==';') continue; // コメント処理 // printf("%s\n", buffer); } fclose(fp); }

1-4-4 スピードコマンドを解析する

以下のソースをtrans2.cというファイル名で作成します。
スピードコマンドを解析する
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MotorNum 9

void translation(char* filename);

main()
{
    translation("forward.msf");
}

// ファイルを読み込んで解釈実行
void translation(char* filename)
{
    FILE *fp;
    char buffer[100];
    int speed;

    fp = fopen(filename, "r");
    while(fscanf(fp,"%s",buffer) >0) {
        if(!strcmp(buffer,"END")) break;    // 終了 //
        if(buffer[0]==';') continue;        // コメント処理 //
if(buffer[0]=='S') { speed = atoi(&buffer[1]); printf("speed = %d\n", speed); }
else printf("%s\n", buffer); } fclose(fp); }

1-4-5 waitコマンドを解析する

以下のソースをtrans3.cというファイル名で作成します。
waitコマンドを解析する
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MotorNum 9

void translation(char* filename);

main()
{
    translation("forward.msf");
}

// ファイルを読み込んで解釈実行
void translation(char* filename)
{
    FILE *fp;
    char buffer[100];
    int speed;

    fp = fopen(filename, "r");
    while(fscanf(fp,"%s",buffer) >0) {
        if(!strcmp(buffer,"END")) break;    // 終了 //
        if(buffer[0]==';') continue;        // コメント処理 //
        if(buffer[0]=='S') {
            speed = atoi(&buffer[1]);
            printf("speed = %d\n", speed);
        }
else if(buffer[0]=='W') { int wait; wait = atoi(&buffer[1]); printf("wait = %d\n", wait); // Sleep(wait); }
else printf("%s\n", buffer); } fclose(fp); }

Sleepを有効にするには#include <windows.h>を付け加える必要がありますが、これを含んでいるcomm.hを後でインクルードするので今のところここはコメントにしておきます。同様にstdlib.h、string.hも必要がなくなります。

1-4-6 スイッチ文に書き換える

以下のソースをtrans4.cというファイル名で作成します。
スイッチ文に書き換える
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MotorNum 9

void translation(char* filename);

main()
{
    translation("forward.msf");
}

// ファイルを読み込んで解釈実行
void translation(char* filename)
{
    FILE *fp;
    char buffer[100];
    int speed, wait;

    fp = fopen(filename, "r");
    while(fscanf(fp,"%s",buffer) >0) {
        if(!strcmp(buffer,"END")) break;    // 終了 //
switch(buffer[0]) { case ';': continue; // コメント処理 // case 'S': speed = atoi(&buffer[1]); printf("speed = %d\n", speed); break; case 'W': wait = atoi(&buffer[1]); printf("wait = %d\n", wait); // Sleep(wait); break; default: printf("%s\n", buffer); }
} fclose(fp); }

1-4-7 サーボモーターの移動量コマンドを分離する

以下のソースをtrans5.cというファイル名で作成します。
サーボモーターの移動量コマンドを分離する
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MotorNum 9

void translation(char* filename);

main()
{
    translation("forward.msf");
}
int split(char buffer[],char *p[]){ int n, m, j; char *q; p[0] = buffer; m=j=1; do { q = strchr(&buffer[m],','); if(!q) break; n = q - buffer; buffer[n]=0; p[j++] = &buffer[m=++n]; } while(1); return j; }
// ファイルを読み込んで解釈実行 void translation(char* filename) { FILE *fp; char buffer[100]; char *p[MotorNum]; int i, n, speed, wait; fp = fopen(filename, "r"); while(fscanf(fp,"%s",buffer) >0) { if(!strcmp(buffer,"END")) break; // 終了 // switch(buffer[0]) { case ';': continue; // コメント処理 // case 'S': speed = atoi(&buffer[1]); printf("speed = %d\n", speed); break; case 'W': wait = atoi(&buffer[1]); printf("wait = %d\n", wait); // Sleep(wait); break;
default: n = split(buffer, p); for(i=0; i<n; i++) printf("%s : ", p[i]);
printf("\n"); } } fclose(fp); }

1-4-8 サーボモーターの移動量をスピードを考慮に入れて求める

以下のソースをtrans6.cというファイル名で作成します。
サーボモーターの移動量をスピードを考慮に入れて求める
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MotorNum 9

void translation(char* filename);

main()
{
    translation("forward.msf");
}

int split(char buffer[],char *p[]){
    int n, m, j;
    char *q;
    p[0] = buffer;
    m=j=1;
    do {
        q = strchr(&buffer[m],',');
        if(!q) break;
        n = q - buffer;
        buffer[n]=0;
        p[j++] = &buffer[m=++n];
    } while(1);
    return j;
}
void doMove(char *p[], int n, int speed) { int i, k, m; int orientation, motor, remain; for(i=0; i<n; i++) { k = atoi(p[i]+2); orientation = (*(p[i]+1) == '+') ? 1: -1; motor = p[i][0]-'0'; // 手抜き // m = (k<speed) ? k : speed; printf("servo %d ", m); remain = k - m; // 残りのステップ数 // printf("残り %d\n", remain); } }
// ファイルを読み込んで解釈実行 void translation(char* filename) { FILE *fp; char buffer[100]; char *p[MotorNum]; int i, n, speed, wait; fp = fopen(filename, "r"); while(fscanf(fp,"%s",buffer) >0) { if(!strcmp(buffer,"END")) break; // 終了// switch(buffer[0]) { case ';': continue; // コメント処理 // case 'S': speed = atoi(&buffer[1]); printf("speed = %d\n", speed); break; case 'W': wait = atoi(&buffer[1]); printf("wait = %d\n", wait); // Sleep(wait); break; default: n = split(buffer, p); for(i=0; i<n; i++) printf("%s : ", p[i]); printf("\n"); doMove(p, n, speed); } } fclose(fp); }

1-4-9 正しい移動量にする

以下のソースをtrans7.cというファイル名で作成します。
正しい移動量にする
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MotorNum 9

void translation(char* filename);

main()
{
    translation("forward.msf");
}

int split(char buffer[],char *p[]){
    int n, m, j;
    char *q;
    p[0] = buffer;
    m=j=1;
    do {
        q = strchr(&buffer[m],',');
        if(!q) break;
        n = q - buffer;
        buffer[n]=0;
        p[j++] = &buffer[m=++n];
    } while(1);
    return j;
}
void doMove(char *p[], int n, int speed) { int i, j, k, m, mm; int orientation, motor, remain, value; for(i=0; i<n; i++) { k = atoi(p[i]+2); orientation = (*(p[i]+1) == '+') ? 1: -1; motor = p[i][0]-'0'; // 手抜き // while(k>=speed){ printf("%d ", speed); k -= speed; } if(k != 0) printf("%d ", k); printf("\n"); } }
// ファイルを読み込んで解釈実行 void translation(char* filename) { FILE *fp; char buffer[100]; char *p[MotorNum]; int i, n, speed, wait; fp = fopen(filename, "r"); while(fscanf(fp,"%s",buffer) >0) { if(!strcmp(buffer,"END")) break; // 終了 // switch(buffer[0]) { case ';': continue; // コメント処理 // case 'S': speed = atoi(&buffer[1]); printf("speed = %d\n", speed); break; case 'W': wait = atoi(&buffer[1]); printf("wait = %d\n", wait); // Sleep(wait); break; default: n = split(buffer, p); for(i=0; i<n; i++) printf("%s : ", p[i]); printf("\n"); doMove(p, n, speed); } } fclose(fp); }

1-4-10 サーボモーターの位置データにする

以下のソースをtrans8.cというファイル名で作成します。
サーボモーターの位置データにする
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MotorNum 9

void translation(char* filename);
int direction[MotorNum]; int now[MotorNum];
main() {
int i; for(i=0; i<MotorNum; i++) { direction[i]=1; // 時計回りと仮定する // now[i] = 110; // 初期値は真ん中 // }
translation("forward.msf"); } // カンマで区切られたデータを一つ一つに分ける // buffer: 入力データ // *p[] 分けられた文字列のポインタ配列 int split(char buffer[],char *p[]){ int n, m, j; char *q; p[0] = buffer; m=j=1; do { q = strchr(&buffer[m],','); if(!q) break; n = q - buffer; buffer[n]=0; p[j++] = &buffer[m=++n]; } while(1); return j; } // サーボモーターを動かす
void doMove(char *p[], int num, int speed) { int i, j, k, n,m, mm, movement; int move; int orientation, motor, remain, value; for(i=0; i<num; i++) { k = atoi(p[i]+2); orientation = (*(p[i]+1) == '+') ? 1: -1; motor = p[i][0]-'0'; // 手抜き // movement = abs(k*orientation-now[motor]+home[motor]); n = movement / speed; // 移動回数 // m = movement % speed; // 余りの移動ステップ // for(j=0; j<n; j++) { move = speed*orientation; now[motor] += move; if(direction[motor]==1) printf("%d:%d ", speed, now[motor]); else printf("%d:%d ", speed, 220-now[motor]); } if(m != 0) { now[motor] = home[motor]+k*orientation; if(direction[motor]==1) printf("%d:%d ", speed, now[motor]); else printf("%d:%d ", speed, 220-now[motor]); } } }
// ファイルを読み込んで解釈実行 void translation(char* filename) { FILE *fp; char buffer[100]; char *p[MotorNum]; int i, n, speed, wait; fp = fopen(filename, "r"); while(fscanf(fp,"%s",buffer) >0) { if(!strcmp(buffer,"END")) break; // 終了 // switch(buffer[0]) { case ';': continue; // コメント処理 // case 'S': speed = atoi(&buffer[1]); printf("speed = %d\n", speed); break; case 'W': wait = atoi(&buffer[1]); printf("wait = %d\n", wait); // Sleep(wait); break; default: n = split(buffer, p); for(i=0; i<n; i++) printf("%s : ", p[i]); printf("\n"); doMove(p, n, speed); } } fclose(fp); }

1-4-11 実際に動く形にする

以下のソースをtrans9.cというファイル名で作成します。
実際に動く形にする
#include <stdio.h>

#include "comm.h"

#define MotorNum 9

void translation(char* filename);

int direction[MotorNum];
int now[MotorNum];

main()
{
    int i;
    for(i=0; i<MotorNum; i++) {
        direction[i]=1;     // 時計回りと仮定する //
        now[i] = 110;       // 初期値は真ん中 //
    }
if(initServo("COM5")) return -1;
translation("forward.msf"); } // カンマで区切られたデータを一つ一つに分ける // buffer: 入力データ // *p[] 分けられた文字列のポインタ配列 int split(char buffer[],char *p[]){ int n, m, j; char *q; p[0] = buffer; m=j=1; do { q = strchr(&buffer[m],','); if(!q) break; n = q - buffer; buffer[n]=0; p[j++] = &buffer[m=++n]; } while(1); return j; } void doMove(char *p[], int num, int speed) { int i, j, k, n,m, mm, movement; int move; int orientation, motor, remain, value; for(i=0; i<num; i++) { k = atoi(p[i]+2); orientation = (*(p[i]+1) == '+') ? 1: -1; motor = p[i][0]-'0'; // 手抜き // movement = abs(k*orientation-now[motor]+home[motor]); n = movement / speed; // 移動回数 // m = movement % speed; // 余りの移動ステップ // for(j=0; j<n; j++) { move = speed*orientation*direction[motor]; now[motor] += move; if(direction[motor]==1) servo(motor, now[motor]); else servo(motor, 220-now[motor]); Sleep(10); } if(m != 0) { now[motor] = home[motor]+k*orientation; if(direction[motor]==1) servo(motor, now[motor]); else servo(motor, 220-now[motor]); Sleep(10); } } } // ファイルを読み込んで解釈実行 void translation(char* filename) { FILE *fp; char buffer[100]; char *p[MotorNum]; int i, n, speed, wait; fp = fopen(filename, "r"); while(fscanf(fp,"%s",buffer) >0) { if(!strcmp(buffer,"END")) break; // 終了 // switch(buffer[0]) { case ';': continue; // コメント処理 // case 'S': speed = atoi(&buffer[1]); printf("speed = %d\n", speed); break; case 'W': wait = atoi(&buffer[1]); printf("wait = %d\n", wait); Sleep(wait); break; default: n = split(buffer, p); for(i=0; i<n; i++) printf("%s : ", p[i]); printf("\n"); doMove(p, n, speed); } } fclose(fp); }


課題

上記read.cのソースファイルとtrans9.cを合体させpuchi.cとして作り、main関数を正しく修正しロボットを動かしなさい。

1-5 18個のサーボモーターをコントロールする

1-5-1 動作チェック

以下のソースをservo2.cというファイル名で作成します。
動作チェック
// プチロボットのサーボモーターのテストプログラム 
// 二つのポートを使ってコントロールする(全部で18個のサーボモータ)
// g++ でコンパイルする

#include 
#include 
#include "comm.h" 

int main(int argc, char** argv)
    {
    int key, end=1;
    int position=110;
    int i, save;
    char *aux[5] = {"COM3", "COM5"};
    int err=0;
    
    if(argc == 2) aux[0][3]=argv[1][0];
    else if(argc ==3) aux[1][3]=argv[1][0];
    if(initServo(aux, 0)) err=1;
    if(initServo(aux, 1)) err+=2;
    if(err) printf("COMポートがおかしい\n");
    if(err==3) {
        printf("COMポートが二つとも使えない\n");
        return -1;
    }
    printf("プチロボットテストプログラム\n"
           "+:10 -:-10 space:1 other:-1 q:終了\n");
    while(end) {
        if(kbhit()) {
            key=readkey();
            save = position;
            switch(key&0x7f){
                case '+': 
                case ';': position+=10; break;
                case '-': position-=10; break;
                case 'q': end=0; break;
                case ' ': position++; break;
                default:  position--;
            }
            if((position <1) || (220<position)) position = save;
            else {
                printf("%d\n", position);
                fflush(stdout);
            }
        }
        for(i=0; i<9; i++) {
           if(!(err & 1)) servo(i, position, 0);
           if(!(err & 2)) servo(i, position, 1);
        }
    }
    if(!(err & 1)) closeServo(0);
    if(!(err & 2)) closeServo(1);
}

課題

puchi.cを元ととし、servo2.cを参考に修正し18個のサーボモーターをコントロールするpuchi2.cを作りなさい。
この時、Puchi_Robo_Config.ini及びmsfファイルを修正する必要があります。

Puchi_Robo_Config.iniについて
  1. {Comm]
    追加のサーボモーターを動かすポートはPort2=2というようにする。
  2. [Direction]と[HomePosition]
    モーターを意味するMの次の数字の代わりに追加のモーターの場合は16進数と似た考えでABC…とします。
    012345678
    ABCDEFGHI
msfファイルについて

Puchi_Robo_Config.iniの[Direction]と同様にモーター番号はABC…とします。