ラズパイを無線ロボットカーにする
ラズパイ(ラズベリーPi)に車輪をつけ、Android端末のアプリ操作で動かします。
これを実現するために、以下のことをおこないます。
また、回路はブレットボードを使用して制作します。
- ラズパイに車輪をつけ動かす
- Androidで操作アプリを作る
ラズパイに車輪をつけ動かす
■ 全景
まずロボットカーにするため、ボディを作成します。
この図では、以下の物を使用しました。
・ラズパイ本体
・ボディ
・電池ボックス(モーター用)
・モバイルバッテリー
・ブレットボード
・ギアボックス
・タイヤ
・Wifi子機
・電子回路パーツ
ラズパイ本体を乗せる筐体を工作し、
モーター付きのギアボックスにタイヤをつけて、
電池ボックス、モバイルバッテリ、ブレットボードを
取り付けます。
Android端末の通信する為に、Wifi子機もUSBに接続します。
■ ブロック図(制御回路のみ)
ギアボックスにはモーターが2個ついていて、
駆動させるために、モータードライブ用ICを使用します。
使用する目的は、ラズパイ本体の電流では容量が足らない為
電流量を増やすためです。
また、電子回路の悪影響を避けるために、モーター用の電源を別に設けます。
モータードライブ用ICは、DRV8835を使用します。
■ pin配線図
GPIOの、10,7,27,22の4本を使用します。
また、GPIO5のSW4は、ラズパイをシャットダウンさせる為のスイッチです。
ラズパイにログインしなくてもシャットダウンさせる為です。
■ DRV8835のpin配線図
GPIOの、10,7,27,22の4本を使用します。
GPIO5のSW4は、ラズパイをシャットダウンさせる為のスイッチです。
ラズパイにログインしなくてもシャットダウンさせる為です。
■ ブレットボード配線の様子
モータードライブ用IC、DRV8835をブレットボードに配置し、
ジャンパーで配線した様子です。
DRV8835の左となりには、SW4を配置しています。
また、左のLEDは赤外線LED送信用ですが、今回は使用しません
■ モーター用電源配線の様子
モーター用電源は単3型電池2個です。
充電池を使用するため、1.2V x 2 = 2.4v になります。
■ ギアボックス設置の様子
モーター駆動のギアボックスは、タミヤのツインギアボックスを使用しました。
■ シャットダウンSWの回路図
ラズパイには、LinxOSが稼働していますので、電源を切るときは
OSのシャットダウンをする必要があります。
(※シャットダウンの必要性はネットで検索)
その時に、シャットダウンSWがあると便利です。
(ラズパイにログインして、シャットダウン操作をしなくていい)
■ シャットダウンのコードの抜粋(C言語)
if ( digitalRead(SW4) == HIGH ) { /* SW4が押下*/
system("/sbin/shutdown -h now"); /*シャットダウンコマンド*/
break;
}
シャットダウンさせるには、
シャットダウンSWが押下されたのを判断し、
OSのシャットダウンコンンドを発行します。
【シャットダウンSWの押下】 -->【シャットダウンコマンド実行】
■ モーター駆動のコードの抜粋(C言語)
#define MOT_LEFT1 10 //左モータ AIN1
#define MOT_LEFT2 17 //左モータ AIN2
#define MOT_REGHT1 27 //右モータ BIN1
#define MOT_REGHT2 22 //右モータ BIN2
//前進
void mt_Forward(void)
{
digitalWrite(MOT_LEFT1, HIGH ); //左モータ AIN1
digitalWrite(MOT_LEFT2, LOW ); //左モータ AIN2
digitalWrite(MOT_REGHT1, HIGH ); //右モータ BIN1
digitalWrite(MOT_REGHT2, LOW ); //右モータ BIN2
}
//後退
void mt_Reverse(void)
{
digitalWrite(MOT_LEFT1, HIGH ); //左モータ AIN1
digitalWrite(MOT_LEFT2, HIGH ); //左モータ AIN2
digitalWrite(MOT_REGHT1, HIGH ); //右モータ BIN1
digitalWrite(MOT_REGHT2, HIGH ); //右モータ BIN2
}
モータードライブ用IC DRV8835は動作モードがあり、
今回はPHASE/ENABLEモードで使用します。
以下、DRV8835の動作表ですが、AOUT1およびAOUT2がHの時
モーターに電流が流れて回転します。
AIN1 | AIN2 | AOUT1 | AOUT2 | FUNCTION |
---|---|---|---|---|
0 | X | L | L | Breake ブレーキ |
1 | 1 | L | H | Reverse 逆転 |
1 | 0 | H | L | Forward 正転 |
速度調節は出来ませんが、簡単に前後左右の動作が制御できました。
前進・後退は、モーター2個を正転・逆転で動かしますが、
左右の旋回は、2個のモーター正転・逆転を逆にします。
動作をまとめると以下のようにしました。
※正転・逆転はモーターの取り付け極性で変わります。
動作 左モータ 右モーター
---------------------------
前進 正転 逆転
後退 逆転 正転
左旋回 逆転 正転
右旋回 正転 逆転
■ Socket受信のコードの抜粋(C言語)
/* 通信ポート・アドレスの設定 */
memset((char *) &server_addr, '\0',sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(skv_port);
server_addr.sin_addr.s_addr = inet_addr("192.168.xxx.xxx");
/* ソケットの生成 */
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("server: socket");
exit(2);
}
memset((char *) &ifr, '\0',sizeof(ifr));
strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name)-1);
ioctl(sockfd, SIOCGIFADDR, &ifr); //IPaddress get
/* ソケットにアドレスを結びつける */
if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("server: bind");
exit(3);
}
/* socket 受信 */
//メインループ
for (;;) {
// クライアントからのコネクト要求待ち
sin_size = sizeof(from_addr);
rdbuf_len = recvfrom( sockfd, rdbuf, sizeof(rdbuf), 0,
(struct sockaddr *)&from_addr, &sin_size ) ;
if(rdbuf_len < 0 ) {
perror( "recvfrom" );
return(2);
}
// コマンド処理
if ( strncmp(rdbuf,"1-fd", rdbuf_len)== 0) {
mt_mv_Forward(); // 前進
~~~~~~~~以下、後退、左旋回、右旋回も同様
}
close(sockfd); /* ソケットを閉鎖 */
ラズパイとAndroid端末は、Wifi通信で接続します。
Wifi通信はTCP/IPのSocket通信で行いますので、
Wifi接続された状態であれば、
通常のSocketプログラムコードで記述できます。
ここでは、Socketから受信したコード値により、
前進後退等の動作をプログラムしていきます。
動作 受信コード
---------------------------
前進 "1-fd"
後退 "2-bk"
左旋回 "3-lf"
右旋回 "4-rg"
Androidで操作アプリを作る
■ Androidアプリ
Androidアプリは、画面上のボタンを押下すると、
socket通信でラズパイにコードを送信するしくみに作成します。
■ Androidアプリの操作画面
アプリの画面構成は、以下のようにします。
※発射ボタンは今回は使用しません
ボタン動作 送信コード
---------------------------
前進 "1-fd"
後退 "2-bk"
左旋回 "3-lf"
右旋回 "4-rg"
■ Socket送信のコードの抜粋(Java言語)
public class MainActivity extends AppCompatActivity {
Button fowardbtn, backbtn,leftbtn, rightbtn, stopbtn, sendbtn;
class UdpSendClass implements Runnable {
String cmd;
public void setCmd(String cmd) {
this.cmd = cmd;
}
@Override
public void run() {
int port = 8000;
//Socket 生成
try {
InetAddress ip = InetAddress.getByName("192.168.xxx.xxx"); //IPアドレス
DatagramSocket Udpsocket = new DatagramSocket();
String strval = cmd;
byte[] strByte = strval.getBytes("UTF-8");
DatagramPacket sendPacket = new DatagramPacket(strByte, strByte.length, ip, port);
Udpsocket.send(sendPacket);
Udpsocket.close();
} catch (Exception e) {
e.printStackTrace();
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "接続に失敗しました。",Toast.LENGTH_LONG).show();
}
});
return;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fowardbtn = findViewById(R.id.fwdbtn_id);
//前進ボタン
fowardbtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
UdpSendClass f_run = new UdpSendClass();
f_run.setCmd("1-fd");
Thread f_run_td = new Thread(f_run);
f_run_td.start();
}
});
上記のコードは、レスポンスの早い、
Socket通信のUDPプロトコルで記述しました。