目次
前
次
エクスカベータシミュレータ(Processing)
Processingでは画面を扱うので、スクリーン仕様を見直します。
レバーのスティック、上下左右の矢印、バケット、ブーム、アームの
アイコンを作成しておきます。
Arduinoから送信されてくるレバースイッチの情報から
アイコンを組み合わせて、操作を画面に表示します。
アイコンは、タイルを敷き詰める仕様とします。
アイコンロードは、次のように書けばよいでしょう。
PImage imgExecute ;
PImage imgIdle ;
PImage imgExit ;
PImage imgLarrow ;
PImage imgRarrow ;
PImage imgUarrow ;
PImage imgDarrow ;
PImage imgArm ;
PImage imgBucket ;
PImage imgBoom ;
PImage imgStick ;
PImage imgNone ;
imgExecute = loadImage("execute.png");
imgIdle = loadImage("idle.png");
imgExit = loadImage("exit.png");
imgLarrow = loadImage("larrow.png");
imgRarrow = loadImage("rarrow.png");
imgUarrow = loadImage("uarrow.png");
imgDarrow = loadImage("darrow.png");
imgArm = loadImage("arm.png");
imgBucket = loadImage("bucket.png");
imgBoom = loadImage("boom.png");
imgStick = loadImage("stick.png");
imgNone = loadImage("none.png");
3種類のレバーを、ユーザーがどの位置において
あるのかを扱うために使う関数を定義。
void show_stick()
{
/* left */
image(imgStick, 40, 40);
/* center left */
image(imgStick,140, 40);
/* center right */
image(imgStick,180, 40);
/* right */
image(imgStick,280, 40);
}
void show_left(int x)
{
image(imgNone, 40, 0);
image(imgNone, 0, 40);
image(imgNone, 80, 40);
image(imgNone, 40, 80);
if ( (x & 8) == 8 ) { image(imgUarrow, 40, 0); }
if ( (x & 4) == 4 ) { image(imgLarrow, 0, 40); }
if ( (x & 2) == 2 ) { image(imgRarrow, 80, 40); }
if ( (x & 1) == 1 ) { image(imgDarrow, 40, 80); }
}
void show_center_left(int x)
{
image(imgNone,140, 0);
image(imgNone,140, 80);
if ( (x & 2) == 2 ) { image(imgUarrow,140, 0); }
if ( (x & 1) == 1 ) { image(imgDarrow,140, 80); }
}
void show_center_right(int x)
{
image(imgNone,180, 0);
image(imgNone,180, 80);
if ( (x & 2) == 2 ) { image(imgUarrow,180, 0); }
if ( (x & 1) == 1 ) { image(imgDarrow,180, 80); }
}
void show_right(int x)
{
image(imgNone,280, 0);
image(imgNone,240, 40);
image(imgNone,320, 40);
image(imgNone,280, 80);
if ( (x & 8) == 8 ) { image(imgUarrow,280, 0); }
if ( (x & 4) == 4 ) { image(imgLarrow,240, 40); }
if ( (x & 2) == 2 ) { image(imgRarrow,320, 40); }
if ( (x & 1) == 1 ) { image(imgDarrow,280, 80); }
}
これらの関数を利用するのは、drawの中にある
シリアル受信のイベントフラグで判断して動作
するシーケンスとします。
今回は、次のように定義しています。
- String型からchar型に変換
- 左レバーの情報を関数show_leftに渡す
- クローラ制御の左レバーの情報を関数show_center_leftに渡す
- クローラ制御の右レバーの情報を関数show_center_rightに渡す
- 右レバーの情報を関数show_rightに渡す
シーケンスをスケッチで使えるコードにすれば、以下。
if ( scflag == true ) {
scflag = false ;
/* check */
stmp = trim(cport.readStringUntil('\n'));
println(stmp);
/* separate */
char[] ss = stmp.toCharArray();
/* perform */
if ( stmp.length() > 2 ) {
/* left */
{
/* clear */
show_left(0);
/* show */
if ( ss[0] != '0' ) {
switch ( ss[0] ) {
/* up */
case '8' : show_left(8); break ;
/* left */
case '4' : show_left(4); break ;
/* right */
case '2' : show_left(2); break ;
/* down */
case '1' : show_left(1); break ;
}
}
}
/* center left */
{
/* clear */
show_center_left(0);
/* up */
if ( ss[1] == '2' ) { show_center_left(2); }
/* down */
if ( ss[1] == '1' ) { show_center_left(1); }
}
/* center right */
{
/* clear */
show_center_right(0);
/* up */
if ( ss[2] == '2' ) { show_center_right(2); }
/* down */
if ( ss[2] == '1' ) { show_center_right(1); }
}
/* right */
{
/* clear */
show_right(0);
/* show */
if ( ss[3] != '0' ) {
switch ( ss[3] ) {
/* up */
case '8' : show_right(8); break ;
/* left */
case '4' : show_right(4); break ;
/* right */
case '2' : show_right(2); break ;
/* down */
case '1' : show_right(1); break ;
}
}
}
}
}
Arduinoからレバーに関係する情報を取得できたなら
アーム、ブーム、バケットのレバー情報があれば
レバーの矢印をつけると共に、アイコンに矢印を追加
します。
- アーム 左右に矢印をつける
- ブーム 上下に矢印をつける
- バケット 左右に矢印をつける
アイコンの横に、矢印のアイコンを置くと考えれば
次のように位置決めすればよいでしょう。
アーム、バケットでは、アイコンをタイルのように
左右に並べていき、矢印をアーム、バケットの垂直
位置より下げて表示。
ブームでは、上下矢印アイコンをおく位置を指定し
上下矢印アイコンのつなぎ目にブームのアイコンを
置けばよいでしょう。
アーム、ブーム、バケットの矢印状態を指定する
専用の関数を定義して利用します。
専用関数の雛形は、以下。
void show_arm_boom_bucket()
{
/* arm */
image(imgArm,?,?);
/* boom */
image(imgBoom,?,?);
/* bucket */
image(imgBucket,?,?);
}
void show_arm(int x)
{
/* no left arrow icon */
/* no right arrow icon */
if ( (x & 2) == 2 ) { /* draw left arrow icon */ }
if ( (x & 1) == 1 ) { /* draw right arrow icon */ }
}
void show_boom(int x)
{
/* no upper arrow icon */
/* no lower arrow icon */
if ( (x & 2) == 2 ) { /* draw upper arrow icon */ }
if ( (x & 1) == 1 ) { /* draw lower arrow icon */ }
}
void show_bucket(int x)
{
/* no left arrow icon */
/* no right arrow icon */
if ( (x & 2) == 2 ) { /* draw left arrow icon */ }
if ( (x & 1) == 1 ) { /* draw right arrow icon */ }
}
雛形を作ったら、各アイコンの表示位置をimageを
利用して定義していきます。
void show_arm_boom_bucket()
{
/* arm */
image(imgArm,40,120);
/* boom */
image(imgBoom,300,140);
/* bucket */
image(imgBucket,360,120);
}
void show_arm(int x)
{
/* no left arrow icon */
image(imgNone, 20,160);
image(imgNone, 60,160);
/* no right arrow icon */
if ( (x & 2) == 2 ) { image(imgLarrow, 20,160); }
if ( (x & 1) == 1 ) { image(imgRarrow, 60,160); }
}
void show_boom(int x)
{
/* no upper arrow icon */
image(imgNone,260,160);
image(imgNone,260,220);
/* no lower arrow icon */
if ( (x & 2) == 2 ) { image(imgUarrow,260,120); }
if ( (x & 1) == 1 ) { image(imgDarrow,260,160); }
}
void show_bucket(int x)
{
/* no left arrow icon */
image(imgNone,340,160);
image(imgNone,380,160);
/* no right arrow icon */
if ( (x & 2) == 2 ) { image(imgLarrow,340,160); }
if ( (x & 1) == 1 ) { image(imgRarrow,380,160); }
}
レバーの方向表示に加えて、アーム、ブーム、バケットの
方向指示を加えます。
if ( scflag == true ) {
scflag = false ;
/* check */
stmp = trim(cport.readStringUntil('\n'));
println(stmp);
/* separate */
char[] ss = stmp.toCharArray();
/* perform */
if ( stmp.length() > 2 ) {
/* left */
{
/* clear */
show_left(0);
/* show */
if ( ss[0] != '0' ) {
switch ( ss[0] ) {
/* up */
case '8' : show_left(8); break ;
/* left */
case '4' : show_left(4); break ;
/* right */
case '2' : show_left(2); break ;
/* down */
case '1' : show_left(1); break ;
}
}
/* arm */
{
/* clear */
show_arm(0);
/* left */
if ( ss[0] == '4' ) { show_arm(2) ; }
/* right */
if ( ss[0] == '2' ) { show_arm(1) ; }
}
}
/* center left */
{
/* clear */
show_center_left(0);
/* up */
if ( ss[1] == '2' ) { show_center_left(2); }
/* down */
if ( ss[1] == '1' ) { show_center_left(1); }
}
/* center right */
{
/* clear */
show_center_right(0);
/* up */
if ( ss[2] == '2' ) { show_center_right(2); }
/* down */
if ( ss[2] == '1' ) { show_center_right(1); }
}
/* right */
{
/* clear */
show_right(0);
/* show */
if ( ss[3] != '0' ) {
switch ( ss[3] ) {
/* up */
case '8' : show_right(8); break ;
/* left */
case '4' : show_right(4); break ;
/* right */
case '2' : show_right(2); break ;
/* down */
case '1' : show_right(1); break ;
}
}
/* boom */
{
/* clear */
show_boom(0);
/* left */
if ( ss[3] == '8' ) { show_boom(1); }
/* right */
if ( ss[3] == '1' ) { show_boom(2); }
}
/* bucket */
{
/* clear */
show_bucket(0);
/* left */
if ( ss[3] == '4' ) { show_bucket(1) ; }
/* right */
if ( ss[3] == '2' ) { show_bucket(2) ; }
}
}
}
}
スクリーンに投影される外界の風景は、後で入れることに
して、ここまでの内容をスケッチにまとめます。
(実際に動かしてデバッグしたので、アイコン表示に関係
する処理は、これまでの説明と異なる場合もあります。)
import processing.serial.*;
Serial cport;
PImage imgExecute ;
PImage imgIdle ;
PImage imgExit ;
PImage imgLarrow ;
PImage imgRarrow ;
PImage imgUarrow ;
PImage imgDarrow ;
PImage imgArm ;
PImage imgBucket ;
PImage imgBoom ;
PImage imgStick ;
PImage imgNone ;
int FRATE = 25 ;
String stmp ;
boolean scflag ;
boolean isRangeOk(int x,int bx,int ex)
{
boolean result ;
/* default */
result = false ;
/* judge */
if ( bx <= x && x <= ex ) { result = true ; }
return result ;
}
void show_btn()
{
image(imgExecute, 80,300);
image(imgIdle ,140,300);
image(imgExit ,220,300);
}
void show_stick()
{
/* left */
image(imgStick, 40, 40);
/* center left */
image(imgStick,140, 40);
/* center right */
image(imgStick,180, 40);
/* right */
image(imgStick,280, 40);
}
void show_left(int x)
{
/* clear */
image(imgNone, 40, 0);
image(imgNone, 0, 40);
image(imgNone, 80, 40);
image(imgNone, 40, 80);
/* arm forward */
if ( (x & 8) == 8 ) { image(imgUarrow, 40, 0); }
/* left */
if ( (x & 4) == 4 ) { image(imgLarrow, 0, 40); }
/* right */
if ( (x & 2) == 2 ) { image(imgRarrow, 80, 40); }
/* arm backward */
if ( (x & 1) == 1 ) { image(imgDarrow, 40, 80); }
}
void show_center_left(int x)
{
/* clear */
image(imgNone,140, 0);
image(imgNone,140, 80);
/* forward */
if ( (x & 2) == 2 ) { image(imgUarrow,140, 0); }
/* backward */
if ( (x & 1) == 1 ) { image(imgDarrow,140, 80); }
}
void show_center_right(int x)
{
/* clear */
image(imgNone,180, 0);
image(imgNone,180, 80);
/* forward */
if ( (x & 2) == 2 ) { image(imgUarrow,180, 0); }
/* backward */
if ( (x & 1) == 1 ) { image(imgDarrow,180, 80); }
}
void show_right(int x)
{
/* clear */
image(imgNone,280, 0);
image(imgNone,240, 40);
image(imgNone,320, 40);
image(imgNone,280, 80);
/* boom up */
if ( (x & 8) == 8 ) { image(imgUarrow,280, 0); }
/* bucket scratch */
if ( (x & 4) == 4 ) { image(imgLarrow,240, 40); }
/* bucket dump */
if ( (x & 2) == 2 ) { image(imgRarrow,320, 40); }
/* boom up */
if ( (x & 1) == 1 ) { image(imgDarrow,280, 80); }
}
void show_arm_boom_bucket()
{
/* arm */
image(imgArm,40,120);
/* boom */
image(imgBoom,300,140);
/* bucket */
image(imgBucket,360,120);
}
void show_arm(int x)
{
/* no left arrow icon */
image(imgNone, 20,160);
image(imgNone, 60,160);
/* no right arrow icon */
if ( (x & 2) == 2 ) { image(imgLarrow, 20,160); }
if ( (x & 1) == 1 ) { image(imgRarrow, 60,160); }
}
void show_boom(int x)
{
/* no upper arrow icon */
image(imgNone,260,160);
image(imgNone,260,120);
/* no lower arrow icon */
if ( (x & 2) == 2 ) { image(imgUarrow,260,120); }
if ( (x & 1) == 1 ) { image(imgDarrow,260,160); }
}
void show_bucket(int x)
{
/* no left arrow icon */
image(imgNone,340,160);
image(imgNone,380,160);
/* no right arrow icon */
if ( (x & 2) == 2 ) { image(imgLarrow,340,160); }
if ( (x & 1) == 1 ) { image(imgRarrow,380,160); }
}
void clear_area()
{
/* black */
fill(0,0,0);
/* clear status area */
rect(390,200,100,140);
}
void show_caption()
{
clear_area();
/* white */
fill(255,255,255);
/* size */
textSize(12);
}
void setup()
{
size(440,340);
/* title caption */
surface.setTitle("Backhow simulator");
/* select framerate */
frameRate(FRATE);
/* select back ground color with BLACK */
background(0,0,0);
//show serial port list (0:COM1 , 1:COM2 ... )
//println(Serial.list());
String arduinoPort = Serial.list()[4];
/* initialize serial port */
cport = new Serial(this,arduinoPort,9600);
cport.clear();
cport.bufferUntil('\n');
scflag = false ;
/* get image */
imgExecute = loadImage("execute.png");
imgIdle = loadImage("idle.png");
imgExit = loadImage("exit.png");
imgLarrow = loadImage("larrow.png");
imgRarrow = loadImage("rarrow.png");
imgUarrow = loadImage("uarrow.png");
imgDarrow = loadImage("darrow.png");
imgArm = loadImage("arm.png");
imgBucket = loadImage("bucket.png");
imgBoom = loadImage("boom.png");
imgStick = loadImage("stick.png");
imgNone = loadImage("none.png");
/* stick */
show_stick();
/* arrow */
show_left(0);
show_center_left(0);
show_center_right(0);
show_right(0);
show_arm_boom_bucket();
show_arm(0);
show_boom(0);
show_bucket(0);
}
void draw()
{
/* button */
show_btn();
/* caption */
show_caption();
/* serial receive */
if ( scflag == true ) {
scflag = false ;
/* check */
stmp = trim(cport.readStringUntil('\n'));
println(stmp);
/* separate */
char[] ss = stmp.toCharArray();
/* perform */
if ( stmp.length() > 2 ) {
/* left */
{
/* clear */
show_left(0);
show_arm(0);
/* show */
if ( ss[0] != '0' ) {
switch ( ss[0] ) {
/* up */
case '8' :
show_left(8);
show_arm(2);
break ;
/* left */
case '4' :
show_left(4);
break ;
/* right */
case '2' :
show_left(2);
break ;
/* down */
case '1' :
show_left(1);
show_arm(1);
break ;
}
}
}
/* center left */
{
/* clear */
show_center_left(0);
/* up */
if ( ss[1] == '2' ) { show_center_left(2); }
/* down */
if ( ss[1] == '1' ) { show_center_left(1); }
}
/* center right */
{
/* clear */
show_center_right(0);
/* up */
if ( ss[2] == '2' ) { show_center_right(2); }
/* down */
if ( ss[2] == '1' ) { show_center_right(1); }
}
/* right */
{
/* clear */
show_right(0);
show_boom(0);
show_bucket(0);
/* show */
if ( ss[3] != '0' ) {
switch ( ss[3] ) {
/* up */
case '8' :
show_right(8);
show_boom(1); /* boom down */
break ;
/* left */
case '4' :
show_right(4);
show_bucket(1);
break ;
/* right */
case '2' :
show_right(2);
show_bucket(2);
break ;
/* down */
case '1' :
show_right(1);
show_boom(2); /* boom up */
break ;
}
}
}
}
}
}
void mouseClicked()
{
if ( mouseButton == LEFT ) {
/* execute */
if ( isRangeOk(mouseX, 80,130) &&
isRangeOk(mouseY,300,320) ) {
/* debug */
println("Execute");
/* send command */
cport.write('E');
cport.write('\r');
}
/* idle */
if ( isRangeOk(mouseX,140,190) &&
isRangeOk(mouseY,300,320) ) {
/* debug */
println("Idle");
/* send command */
cport.write('I');
cport.write('\r');
}
/* exit */
if ( isRangeOk(mouseX,220,390) &&
isRangeOk(mouseY,300,320) ) {
/* send command */
cport.write('I');
cport.write('\r');
exit();
}
}
/* Exit */
if ( mouseButton == RIGHT ) {
/* send command */
cport.write('I');
cport.write('\r');
exit();
}
}
void serialEvent(Serial p)
{
scflag = true ;
}
このスケッチのテスト、デバッグに利用したArduinoスケッチは、以下。
#include <MsTimer2.h>
#define OFF 0
#define ON OFF+1
#define LASTSTATE 10
#define LASTXCNT 10
#define LED_BIT 5
#define NPER 100
#define MASK0F 0x0f
/* variables */
byte uflag ;
byte tflag ;
byte eflag ;
byte cmd ;
byte sindex ;
byte sbuf[4] ;
byte state ;
byte xcnt ;
char msg[5];
char ll[10];
char cl[10];
char cr[10];
char rr[10];
/* function prototype */
void show_help();
char get_asc(byte x);
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void send_led(byte x);
void update_trigger(void);
void setup()
{
/* initialize serial */
Serial.begin(9600);
sindex = 0 ;
/* clear flags */
uflag = OFF ;
tflag = OFF ;
eflag = OFF ;
/* initialize */
state = 0 ;
xcnt = 0 ;
*(msg+4) = '\0' ;
/* initialize port values */
PORTB = 0xf0 ;
PORTC = 0xf8 ;
PORTD = 0x01 ;
/* initialize port direction */
DDRB = 0xff ;
DDRC = 0x07 ;
DDRD = 0xfe ;
/* initialize left and right */
*(ll+0) = '0' ; *(rr+0) = '0' ;
*(ll+1) = '0' ; *(rr+1) = '0' ;
*(ll+2) = '1' ; *(rr+2) = '8' ;
*(ll+3) = '1' ; *(rr+3) = '8' ;
*(ll+4) = '2' ; *(rr+4) = '4' ;
*(ll+5) = '2' ; *(rr+5) = '4' ;
*(ll+6) = '4' ; *(rr+6) = '2' ;
*(ll+7) = '4' ; *(rr+7) = '2' ;
*(ll+8) = '8' ; *(rr+8) = '1' ;
*(ll+9) = '8' ; *(rr+9) = '1' ;
/* initialize center left and center right */
*(cl+0) = '0' ; *(cr+0) = '0' ;
*(cl+1) = '0' ; *(cr+1) = '1' ;
*(cl+2) = '1' ; *(cr+2) = '0' ;
*(cl+3) = '1' ; *(cr+3) = '2' ;
*(cl+4) = '2' ; *(cr+4) = '0' ;
*(cl+5) = '2' ; *(cr+5) = '1' ;
*(cl+6) = '0' ; *(cr+6) = '0' ;
*(cl+7) = '0' ; *(cr+7) = '2' ;
*(cl+8) = '1' ; *(cr+8) = '0' ;
*(cl+9) = '1' ; *(cr+9) = '0' ;
/* trigger period */
MsTimer2::set(NPER,update_trigger);
/* enable */
MsTimer2::start();
}
void loop()
{
/* transfer switch state */
if ( tflag == ON ) {
/* clear event flag */
tflag = OFF ;
/* generate code */
*(msg+0) = *(ll+state) ;
*(msg+1) = *(cl+state) ;
*(msg+2) = *(cr+state) ;
*(msg+3) = *(rr+state) ;
/* update */
state++ ;
if ( state == LASTSTATE ) { state = 0 ; }
/* judge transfer */
if ( eflag == ON ) {
rs_puts( msg );
crlf();
}
}
/* command interpreter */
if ( uflag == ON ) {
/* clear event flag */
uflag = OFF ;
/* new line */
crlf();
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help() ; }
/* execute */
if ( cmd == 'E' ) {
eflag = ON ;
state = 0 ;
}
/* idle */
if ( cmd == 'I' ) { eflag = OFF ; }
/* show status */
if ( cmd == 'S' ) {
/* message */
if ( eflag == ON ) { rs_puts("run"); }
else { rs_puts("idle"); }
/* new line */
crlf();
}
}
}
/* receive interrupt */
void serialEvent()
{
char ch;
if ( Serial.available() > 0 ) {
/* get 1 character */
ch = Serial.read();
/* store */
*(sbuf+sindex) = ch ;
/* increment */
sindex++ ;
/* judge */
if ( ch == '\r' ) {
sindex = 0 ;
uflag = ON ;
}
}
}
char get_asc(byte x)
{
byte result ;
/* default */
result = '0' ;
/* judge */
if ( x < 10 ) { result = x + '0' ; }
if ( 9 < x ) { result = x - 10 + 'A' ; }
return result ;
}
void show_help()
{
rs_puts("? help"); crlf();
rs_puts("E execute"); crlf();
rs_puts("I idle"); crlf();
rs_puts("S show status"); crlf();
}
void rs_putchar(char x)
{
Serial.write(x);
}
void rs_puts(char *ptr)
{
while ( *ptr ) {
rs_putchar( *ptr );
ptr++;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
void send_led(byte x)
{
if ( x ) { PORTB |= (1 << LED_BIT) ; }
else { PORTB &= ~(1 << LED_BIT) ; }
}
void update_trigger(void)
{
/* led handling */
send_led(xcnt & ON);
/* update counter */
xcnt++ ;
/* judge */
if ( xcnt == LASTXCNT ) {
xcnt = 0 ;
tflag = ON ;
}
}
中央スクリーンで、前後に移動した場合と運転台を
左右に回転した場合の処理を定義します。
十文字のアイコンを利用し、前後の移動ではアイコンが
大きくなるか小さくなるかで表現します。
回転の場合、アイコンサイズは変えないで左右に移動
させます。
車体を中央にあるレバーで前後に移動する他に、信地
旋回する場合には、アイコンは左右に移動することに
加えて、上下動を与えます。
スクリーン上のアイコンは、次の3種の操作を
組み合わせて表示すればよいでしょう。
- サイズ変更(竿の長さを変える)
- 左右に平行移動
- 上下に平行移動
十文字アイコンは、縦と横の竿が交差する点を
左右、上下に平行移動後、竿を描けばいけそう
です。サイズ変更は、竿の長さを変える処理で
対応できます。
スクリーンのサイズは固定なので、アイコンがはみ
出す場合には、竿を描画しなければよいはず。
(under construction)
目次
前
次