...

понедельник, 10 февраля 2014 г.

РОБОТ на базе: android, arduino, bluetooth. Рефлексный. Часть 2

В прошлой статье РОБОТ на базе: android, arduino, bluetooth. Начало была предложена общая схема робота и представлена технология передачи и приема данных между андроидом и ардуино. А в ее завершении приведен список заказанных деталей и модулей. Детали получены (рис.1), комментарии учтены, приступим к созданию первого робота – рефлексного робота.



Рисунок 1



Оглавление




Статья 1. РОБОТ на базе: android, arduino, bluetooth. Начало

Статья 2. РОБОТ на базе: android, arduino, bluetooth. Рефлексный. Часть 2.

Предполагается, что человек читающий статью уже имеет представление о:

-Базовых понятиях электроники

-Предыдущей статье


Постановка задачи




Создать робота который выполняет следующий функционал:

-Имеет удаленное управление при помощи смартфона (передвижение вперед, назад, налево, направо)

-Передает на смартфон данные о расстоянии до объекта находящегося перед ним(на базе ультра звукового датчика)

-Имеет режим автономного управления: непрерывно перемещается по помещению, при встрече препятствий меняет направление своего движения, тем самым объезжая препятствие.

Немного теории




Наш мир является сложнейшей системой, в которой взаимодействуют между собой огромное количество объектов, подчиненных определенным законам физики, поэтому создание робота функционирующего в рамках этой системы, является очень трудоемкой задачей. Для упрощения процесса создания первого робота воспользуемся понятием абстрагирования среды(в которую помещен робот) и действий робота. В дальнейших статьях будем усложнять среду и соответственно действия робота.
Абстрагирование среды



Среда где будет обитать наш первый робот будет представлять собой двухмерный мир и обладать следующими характеристиками:

1) Полностью наблюдаемая, т.е. датчики робота предоставляют доступ к полной информации о состоянии среды в каждый момент времени. Полностью наблюдаемые варианты среды являются удобными, поскольку роботу не требуется поддерживать какое-либо внутреннее состояние для того, чтобы быть в курсе всего происходящего в этом мире.

2) Детерминированная. Если следующее состояние среды полностью определяется текущим состоянием и действием, выполненным роботом, то такая среда называется детерминированной; в противном случае она является стохастической.

3) Эпизодическая. В эпизодической проблемной среде опыт робота состоит из неразрывных эпизодов. Каждый эпизод включает в себя восприятие среды роботом, а затем выполнение одного действия. При этом крайне важно то, что следующий эпизод не зависит от действий, предпринятых в предыдущих эпизодах. В эпизодических вариантах среды выбор действия в каждом эпизоде зависит только от самого эпизода.

4) Статическая. Если среда может измениться в ходе того, как робот выбирает очередное действие, то такая среда называется динамической для данного робота; в противном случае она является статической.

5) Непрерывная — Различие между дискретными и непрерывными вариантами среды может относиться к состоянию среды, способу учета времени, а также восприятием и действиям агента. В нашем случае считается что состояние среды меняется непрерывно. К примеру игра в шахматы является дискретной, так как имеет конечное количество различных состояний.

6) Одноагентная это среда в которой находится один объект(робот), и другие объекты на него не влияют и не конкурируют с ним.
Абстрагирование действий робота



1) Движение – робот может передвигаться в двух направлениях(взад, вперед) и разворачиваться на месте(налево, направо)

2) Датчики робота (ультразвуковой сенсор), позволяет определить расстояние до объекта. Расстояние может быть определено от 0,02 метра до 4 метров.

Таким образом, определим, что создаваемый в этой статье робот является простым рефлексным роботом. Подобные роботы выбирают действия на основе текущего акта восприятия, игнорируя всю остальную историю актов восприятия.


Краткая информация по используемым деталям и модулям




Драйвер двигателей HG7881. Для управления двигателями робота необходимо устройство, которое бы преобразовывало управляющие сигналы малой мощности в токи, достаточные для управления моторами. Такое устройство называют драйвером двигателей.



HG7881 – это двухканальный драйвер двигателей, питание возможно от источника 2,5 – 12 В. Описание выходов драйвера:

Таблица 1






























ВыводОписание
B-IAДвигатель B Вход A (IA)
B-IBДвигатель B Вход B (IB)
GNDЗемля (Минус)
VCCРабочее напряжение 2.5-12V (Плюс)
A-IAДвигатель A Вход A (IA)
A-IBДвигатель A Вход B (IB)



Для того чтобы заставить двигатели работать нужным нам образом на выводы (B-IA, B-IB, A-IA, A-IB) необходимо подавать логические сигналы (HIGH,LOW). Таблица истинности двигателей:

Таблица 2



























IAIBСостояние двигателя
LLOff
HLForward
LHReverse
HHOff



Ультразвуковой сенсор измерения расстояния HC-SR04. Определяет расстояние до объекта, измеряя время отражения звуковой волны от объекта.



Сенсор излучает короткий ультразвуковой импульс (в момент времени 0), который отражается от объекта и принимается сенсором. Расстояние рассчитывается исходя из времени до получения эха и скорости звука в воздухе.

На вывод (Trig) подаётся импульс длительностью 10 мкс, ультразвуковой модуль излучает 8 пачек ультразвукового сигнала с частотой 40кГц и обнаруживает их эхо. Измеренное расстояние до объекта пропорционально ширине эха (Echo) и может быть рассчитано по формуле:

Ширина импульса/58 = расстояние в см.

Сборка робота и подключение всех модулей




Собираем платформу (рис.2).



Рисунок 2

Подключаем двигатели к драйверу (рис. 3). По два двигателя на один разъем драйвера, т.е. двигатели левой стороны платформы к разъему “Motor B”, двигатели правой стороны — “Motor A”. Управление платформой будет произведено аналогично гусеничной. При движении вперед и назад все двигатели работают синхронно в одном направлении, при повороте направо двигатели правой стороны платформы останавливаются, а левой двигаются синхронно, при повороте налево двигатели левой стороны останавливаются, а правой двигаются синхронно.



Рисунок 3

Прикручиваем верхнюю часть платформы. Соединяем драйвер двигателей, ардуино, аккумуляторы, БТ модуль и ультразвуковой сенсор к макетной плате (рис.4)



Рисунок 4

Схема подключения представлена на (рис.5). Питание ардуино, ультразвукового сенсора и драйвера двигателей (следовательно и самих двигателей) обеспечивают 4 подключенных последовательно аккумулятора (1,2 В., 2700 мА/ч), на БТ модуль питание подается от выхода ардуино 3,3 В.



Рисунок 5

Робот собран, необходимо его заставить выполнять команды, отправленные с андроида.

Скетч для Arduino ШАГ 1 – Удаленное управление роботом




Загрузим скетч в ардуино, не забыв перед этим отключить питания от БТ модуля (в противном случае загрузить его не удастся):

Скетч для Arduino ШАГ 1


//Объявляем переменные
char incomingbyte; // переменная для приема данных
//motors A (RIGHT)
int R_A_IA = 9; // A-IA
int R_A_IB = 10; // A-IB
//motors B (LEFT)
int L_B_IA = 11; // B-IA
int L_B_IB = 12; // B-IB
//Инициализация переменных
void setup() {
Serial.begin(38400);
//motors RIGHT
pinMode(R_A_IA,OUTPUT);
digitalWrite(R_A_IA, HIGH);
pinMode(R_A_IB,OUTPUT);
digitalWrite(R_A_IB, HIGH);
//motors LEFT
pinMode(L_B_IA,OUTPUT);
digitalWrite(L_B_IA, HIGH);
pinMode(L_B_IB,OUTPUT);
digitalWrite(L_B_IB, HIGH);
}

void go_forward(){
//motors LEFT
digitalWrite(L_B_IA, LOW);
digitalWrite(L_B_IB, HIGH);
//motors RIGHT
digitalWrite(R_A_IA, LOW);
digitalWrite(R_A_IB, HIGH);
}

void go_back(){
//motors LEFT
digitalWrite(L_B_IA, HIGH);
digitalWrite(L_B_IB, LOW);
//motors RIGHT
digitalWrite(R_A_IA, HIGH);
digitalWrite(R_A_IB, LOW);
}

void go_right(){
//motors LEFT
digitalWrite(L_B_IA, LOW);
digitalWrite(L_B_IB, HIGH);
//motors RIGHT
digitalWrite(R_A_IA, LOW);
digitalWrite(R_A_IB, LOW);
}

void go_left(){
//motors LEFT
digitalWrite(L_B_IA, LOW);
digitalWrite(L_B_IB, LOW);
//motors RIGHT
digitalWrite(R_A_IA, LOW);
digitalWrite(R_A_IB, HIGH);
}

void stop_robot(){
//motors LEFT
digitalWrite(L_B_IA, LOW);
digitalWrite(L_B_IB, LOW);
//motors RIGHT
digitalWrite(R_A_IA, LOW);
digitalWrite(R_A_IB, LOW);
}

//Основной цикл программы
void loop() {
if (Serial.available() > 0){
incomingbyte = Serial.read();
if (incomingbyte == '1'){
go_forward();
Serial.println("FORWARD");
}
if (incomingbyte == '2'){
go_back();
Serial.println("BACK");
}
if (incomingbyte == '3'){
go_right();
Serial.println("RIGHT");
}
if (incomingbyte == '4'){
go_left();
Serial.println("LEFT");
}
if (incomingbyte=='0'){
stop_robot();
Serial.println("STOP");
}
}
}







Объявляем переменные: R_A_IA, R_A_IB – определяют номера выводов управляющих двигателем А (двигатели правой стороны), L_B_IA, L_B_IB — выводы управляющие двигателем B(двигатели левой стороны. Инициируем последовательное соединение и задаем скорость передачи данных в бит/c (бод) – 38400. Устанавливаем режим работы выводов управляющих двигателями – OUTPUT (выходы). Подаем на все выходы значение HIGH, что означает — двигатели отключены(таблица 2).

Определяем функции: go_forward(), go_back(), go_right(), go_left(), stop_robot(), которые запускают двигатели в прямом или обратном направлении вращения, тем самым приводя робота в движение – вперед, назад, направо, налево, стоп, соответственно.

В основном цикле программы происходит считывание и обработка данных полученных в последовательный порт от БТ модуля. В зависимости от полученной команды выполняется та или иная функция и по последовательному порту передается текст об ее выполнении.

Android приложение ШАГ 1 – Удаленное управление роботом




Создаем новый проект «Android application project». Как было написано в прошлой статье, для работы с БТ необходимо выставить права на использование его нашим приложением. Для этого заходим в манифест, выбираем закладку Permissions, нажимаем add, далее Uses permission, и устанавливаем следующие права: android.permission.BLUETOOTH, android.permission.BLUETOOTH_ADMIN

Создаем основное activity, в res/layout/activity_main.xml поместим код:

основное activity ШАГ 1


<LinearLayout xmlns:android="http://ift.tt/nIICcg"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<TextView
android:id="@+id/txtrobot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Поле текстовых сообщений" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<Button
android:id="@+id/b1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="100"
android:text="Вперед" />

</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="100" >

<Button
android:id="@+id/b4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="100"
android:text="Налево" />

<Button
android:id="@+id/b0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="100"
android:text="Стоп" />

<Button
android:id="@+id/b3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="100"
android:text="Направо" />

</LinearLayout>

</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<Button
android:id="@+id/b2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="100"
android:text="Назад" />

</LinearLayout>

</LinearLayout>







Вот так будет выглядеть основное activity:



Текстовое поле «txtrobot», будет отображать всю необходимую нам информацию. Кнопки b0, b1, b2, b3, b4 будут отправлять команды в arduino (0, 1, 2, 3, 4)

Переходим в src/../MainActivity.java здесь и будет располагаться наш основной код.

В предыдущей статье на шаге 4, был представлен код позволяющий передавать и принимать данные по БТ. За основу возьмем этот код.

В состояния активити onPause() и onResume() добавим условие проверки существования БТ у андроида и определения включен ли он. В предыдущей статье это условие отсутствовало в связи, с чем при запуске приложения, если был отключен БТ, оно завершалось с ошибкой и только после этого предлагало включить БТ.

if (btAdapter != null){
if (btAdapter.isEnabled()){
//Код приложения
}
}




Объявим переменные для хранения кнопок:

Button b0, b1, b2, b3, b4;




Находим их по ID:

b0 = (Button) findViewById(R.id.b0);//Стоп
b1 = (Button) findViewById(R.id.b1);//Вперед
b2 = (Button) findViewById(R.id.b2);//Назад
b3 = (Button) findViewById(R.id.b3);//Направо
b4 = (Button) findViewById(R.id.b4);//Налево




Напишем обработчики нажатия этих кнопок, для отправки команд:

b0.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("0");
}
});

b1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("1");
}
});

b2.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("2");
}
});

b3.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("3");
}
});

b4.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("4");
}
});




Полный код приложения:

Код приложения Шаг 1


package com.example.rob_2_3_0;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

import com.example.rob_2_3_0.R;

import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.bluetooth.*;
import android.content.Intent;

public class MainActivity extends Activity {
private static final int REQUEST_ENABLE_BT = 1;
final int ArduinoData = 1;
final String LOG_TAG = "myLogs";
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private static String MacAddress = "20:11:02:47:01:60"; // MAC-адрес БТ модуля
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private ConnectedThred MyThred = null;
public TextView mytext;
Button b0, b1, b2, b3, b4;
boolean fl=false;
Handler h;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

btAdapter = BluetoothAdapter.getDefaultAdapter();
mytext = (TextView) findViewById(R.id.txtrobot);

if (btAdapter != null){
if (btAdapter.isEnabled()){
mytext.setText("Bluetooth включен. Все отлично.");
}else
{
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

}else
{
MyError("Fatal Error", "Bluetooth ОТСУТСТВУЕТ");
}

b0 = (Button) findViewById(R.id.b0);//Стоп
b1 = (Button) findViewById(R.id.b1);//Вперед
b2 = (Button) findViewById(R.id.b2);//Назад
b3 = (Button) findViewById(R.id.b3);//Направо
b4 = (Button) findViewById(R.id.b4);//Налево

b0.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("0");
}
});

b1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("1");
}
});

b2.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("2");
}
});

b3.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("3");
}
});

b4.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("4");
}
});

h = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case ArduinoData:
byte[] readBuf = (byte[]) msg.obj;
String strIncom = new String(readBuf, 0, msg.arg1);
mytext.setText("Данные от Arduino: " + strIncom);
break;
}
};
};
}

@Override
public void onResume() {
super.onResume();
if (btAdapter != null){
if (btAdapter.isEnabled()){
BluetoothDevice device = btAdapter.getRemoteDevice(MacAddress);
Log.d(LOG_TAG, "***Получили удаленный Device***"+device.getName());

try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
Log.d(LOG_TAG, "...Создали сокет...");
} catch (IOException e) {
MyError("Fatal Error", "В onResume() Не могу создать сокет: " + e.getMessage() + ".");
}

btAdapter.cancelDiscovery();
Log.d(LOG_TAG, "***Отменили поиск других устройств***");

Log.d(LOG_TAG, "***Соединяемся...***");
try {
btSocket.connect();
Log.d(LOG_TAG, "***Соединение успешно установлено***");
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
MyError("Fatal Error", "В onResume() не могу закрыть сокет" + e2.getMessage() + ".");
}
}

MyThred = new ConnectedThred(btSocket);
MyThred.start();
}
}
}

@Override
public void onPause() {
super.onPause();

Log.d(LOG_TAG, "...In onPause()...");

if (btAdapter != null){
if (btAdapter.isEnabled()){
if (MyThred.status_OutStrem() != null) {
MyThred.cancel();
}
try {
btSocket.close();
} catch (IOException e2) {
MyError("Fatal Error", "В onPause() Не могу закрыть сокет" + e2.getMessage() + ".");
}
}
}
}//OnPause

private void MyError(String title, String message){
Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
finish();
}


//Отдельный поток для передачи данных
private class ConnectedThred extends Thread{
private final BluetoothSocket copyBtSocket;
private final OutputStream OutStrem;
private final InputStream InStrem;

public ConnectedThred(BluetoothSocket socket){
copyBtSocket = socket;
OutputStream tmpOut = null;
InputStream tmpIn = null;
try{
tmpOut = socket.getOutputStream();
tmpIn = socket.getInputStream();
} catch (IOException e){}

OutStrem = tmpOut;
InStrem = tmpIn;
}

public void run()
{
byte[] buffer = new byte[1024];
int bytes;

while(true){
try{
bytes = InStrem.read(buffer);
h.obtainMessage(ArduinoData, bytes, -1, buffer).sendToTarget();
}catch(IOException e){break;}

}

}

public void sendData(String message) {
byte[] msgBuffer = message.getBytes();
Log.d(LOG_TAG, "***Отправляем данные: " + message + "***" );

try {
OutStrem.write(msgBuffer);
} catch (IOException e) {}
}

public void cancel(){
try {
copyBtSocket.close();
}catch(IOException e){}
}

public Object status_OutStrem(){
if (OutStrem == null){return null;
}else{return OutStrem;}
}
}
}








Данное приложение, позволяет управлять роботом при помощи андроида, отправляя команды по БТ на ардуино, и принимая текстовые ответы от него. Первая часть поставленной задачи выполнена.

Скетч для Arduino ШАГ 2 – Режим автономного управления роботом




Для работы с ультразвуковым сенсором, воспользуемся готовой библиотекой

ultrasonic-HC-SR04.zip

Распаковываем файлы и помещаем в каталог где расположен скетч

Подключаем библиотеку

#include "Ultrasonic.h"




Конструктор Ultrasonic принимает два параметра — номера выводов к которым подключены Trig и Echo, соответственно:

Ultrasonic ultrasonic(5, 6);




Получаем данные о расстоянии до объекта в сантиметрах:

float dist_cm = ultrasonic.Ranging(CM); //Получаем расстояние до объекта




Передаем данные на последовательный порт, для последующей передачи их через БТ модуль.

Serial.print("*");
Serial.print(dist_cm);
Serial.print("#");




Символы «*» и «#» означают начало и конец передаваемого блока информации о расстоянии до объекта. Это необходимо для того чтобы четко отделять необходимые данные друг от друга, так как при их передачи часть теряется либо приходит с запозданием.

Полный скетч для загрузки в ардуино:

Скетч для Arduino ШАГ 2



#include "Ultrasonic.h"
//Объявляем переменные
char incomingbyte;
int i=0;
//motors A (LEFT)
int R_A_IA = 9; // A-IA
int R_A_IB = 10; // A-IB
//motors B (RIGHT)
int L_B_IA = 11; // B-IA
int L_B_IB = 12; // B-IB
//Конструктор для работы с ультразвуковым сенсором
Ultrasonic ultrasonic(5, 6);
//Инициализация переменных
void setup() {
Serial.begin(38400);
//RIGHT
pinMode(R_A_IA,OUTPUT);
digitalWrite(R_A_IA, HIGH);
pinMode(R_A_IB,OUTPUT);
digitalWrite(R_A_IB, HIGH);
//LEFT
pinMode(L_B_IA,OUTPUT);
digitalWrite(L_B_IA, HIGH);
pinMode(L_B_IB,OUTPUT);
digitalWrite(L_B_IB, HIGH);
}

void go_forward(){
//LEFT
digitalWrite(L_B_IA, LOW);
digitalWrite(L_B_IB, HIGH);
//RIGHT
digitalWrite(R_A_IA, LOW);
digitalWrite(R_A_IB, HIGH);
}

void go_back(){
//LEFT
digitalWrite(L_B_IA, HIGH);
digitalWrite(L_B_IB, LOW);
//RIGHT
digitalWrite(R_A_IA, HIGH);
digitalWrite(R_A_IB, LOW);
}

void go_right(){
//LEFT
digitalWrite(L_B_IA, LOW);
digitalWrite(L_B_IB, HIGH);
//RIGHT
digitalWrite(R_A_IA, LOW);
digitalWrite(R_A_IB, LOW);
}

void go_left(){
//LEFT
digitalWrite(L_B_IA, LOW);
digitalWrite(L_B_IB, LOW);
//RIGHT
digitalWrite(R_A_IA, LOW);
digitalWrite(R_A_IB, HIGH);
}

void stop_robot(){
//LEFT
digitalWrite(L_B_IA, LOW);
digitalWrite(L_B_IB, LOW);
//RIGHT
digitalWrite(R_A_IA, LOW);
digitalWrite(R_A_IB, LOW);
}

//Основной цикл программы
void loop() {
if (Serial.available() > 0){
incomingbyte = Serial.read();
if (incomingbyte == '1'){
go_forward();
}
if (incomingbyte == '2'){
go_back();
}
if (incomingbyte == '3'){
go_right();
}
if (incomingbyte == '4'){
go_left();
}
if (incomingbyte=='0'){
stop_robot();
}
}
float dist_cm = ultrasonic.Ranging(CM); //Получаем расстояние до объекта
Serial.print("*");
Serial.print(dist_cm);
Serial.print("#");
}






Android приложение ШАГ 2 – Режим автономного управления роботом




Добавим в основное активити кнопку «b5» (автоуправление). Код приведен ниже:

основное activity ШАГ 2



<LinearLayout xmlns:android="http://ift.tt/nIICcg"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<Button
android:id="@+id/b5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Автоуправление" />
</LinearLayout>

<TextView
android:id="@+id/txtrobot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Поле текстовых сообщений" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<Button
android:id="@+id/b1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="100"
android:text="Вперед" />

</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="100" >

<Button
android:id="@+id/b4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="100"
android:text="Налево" />

<Button
android:id="@+id/b0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="100"
android:text="Стоп" />

<Button
android:id="@+id/b3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="100"
android:text="Направо" />

</LinearLayout>

</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<Button
android:id="@+id/b2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="100"
android:text="Назад" />

</LinearLayout>

</LinearLayout>





Содержимое




Таким образом, основное activity примет вид:



Объявим переменную b5:

Button b0, b1, b2, b3, b4, b5;




И флаг позволяющий определить включен режим автоуправления или нет:

boolean fl=false;




Находим ее по ID:

b5 = (Button) findViewById(R.id.b5);//Автоуправление




Создадим обработчик ее нажатия:

b5.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Log.d(LOG_TAG, "НАЖАЛИ АВТОУПРАВЛЕНИЕ");
if (!fl){
Log.d(LOG_TAG, "Если флаг опущен");
fl=true;
b1.setEnabled(false);
b2.setEnabled(false);
b3.setEnabled(false);
b4.setEnabled(false);
MyThred.sendData("1");
Log.d(LOG_TAG, "Отправили 1");
}
}
});




А также внесем изменения в обработчик кнопки «b0»(Стоп)

b0.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("0");
if (fl)
{
fl = false;
b1.setEnabled(true);
b2.setEnabled(true);
b3.setEnabled(true);
b4.setEnabled(true);
}
}
});


Осталось создать алгоритм позволяющий роботу самостоятельно перемещаться по помещению и объезжать препятствия.

Обработаем полученные данные о расстоянии до объекта отправленные ардуино. Если расстояние до объекта менее 50 см. то поворачиваем направо в противном случае едим прямо:



byte[] readBuf = (byte[]) msg.obj;
String strIncom = new String(readBuf, 0, msg.arg1);
sb.append(strIncom);// формируем строку
int beginOfLineIndex = sb.indexOf("*");//определяем символы начала строки
int endOfLineIndex = sb.indexOf("#");//определяем символы конца строки
//Если блок данных соответствует маске *данные# то выполняем код
if ((endOfLineIndex > 0) && (beginOfLineIndex == 0)) { String sbprint = sb.substring(beginOfLineIndex+1, endOfLineIndex-3);
mytext.setText("Данные от Arduino: " + sbprint);
if (fl){
int dist = Integer.parseInt(sbprint);
if (dist<50)
{
MyThred.sendData("3");

}
else
{
MyThred.sendData("1");
}
}
}
sb.delete(0, sb.length());





Ниже приведен полный код Activity:

Код приложения Шаг 2


package com.robot.rob_2_3;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.UUID;

import com.robot.rob_2_3.R;

import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.bluetooth.*;
import android.content.Intent;

public class MainActivity extends Activity {
private static final int REQUEST_ENABLE_BT = 1;
final int ArduinoData = 1;
final String LOG_TAG = "myLogs";
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private static String MacAddress = "20:11:02:47:01:60"; // MAC-адрес БТ модуля
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final long MILLIS_PER_SECOND = 0;
private ConnectedThred MyThred = null;
public TextView mytext;
Button b0, b1, b2, b3, b4, b5;
boolean fl=false;
Handler h;
private StringBuilder sb = new StringBuilder();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

btAdapter = BluetoothAdapter.getDefaultAdapter();
mytext = (TextView) findViewById(R.id.txtrobot);

if (btAdapter != null){
if (btAdapter.isEnabled()){
mytext.setText("Bluetooth включен. Все отлично.");
}else
{
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

}else
{
MyError("Fatal Error", "Bluetooth ОТСУТСТВУЕТ");
}

b0 = (Button) findViewById(R.id.b0);//Стоп
b1 = (Button) findViewById(R.id.b1);//Вперед
b2 = (Button) findViewById(R.id.b2);//Назад
b3 = (Button) findViewById(R.id.b3);//Направо
b4 = (Button) findViewById(R.id.b4);//Налево
b5 = (Button) findViewById(R.id.b5);//Автоуправление

b0.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("0");
if (fl)
{
fl = false;
b1.setEnabled(true);
b2.setEnabled(true);
b3.setEnabled(true);
b4.setEnabled(true);
}
}
});

b1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("1");
}
});

b2.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("2");
}
});

b3.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("3");
}
});

b4.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("4");
}
});

b5.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Log.d(LOG_TAG, "НАЖАЛИ АВТОУПРАВЛЕНИЕ");
if (!fl){
Log.d(LOG_TAG, "Если флаг опущен");
fl=true;
b1.setEnabled(false);
b2.setEnabled(false);
b3.setEnabled(false);
b4.setEnabled(false);
MyThred.sendData("1");
Log.d(LOG_TAG, "Отправили 1");
}
}
});

h = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case ArduinoData:
byte[] readBuf = (byte[]) msg.obj;
String strIncom = new String(readBuf, 0, msg.arg1);
sb.append(strIncom);// формируем строку
int beginOfLineIndex = sb.indexOf("*");//определяем символы начала строки
int endOfLineIndex = sb.indexOf("#");//определяем символы конца строки
//Если блок данных соотвествует маске *данные# то выполняем код
if ((endOfLineIndex > 0) && (beginOfLineIndex == 0)) { // если встречаем конец строки,
String sbprint = sb.substring(beginOfLineIndex+1, endOfLineIndex-3); // то извлекаем строку
mytext.setText("Данные от Arduino: " + sbprint);
if (fl){
int dist = Integer.parseInt(sbprint);
if (dist<50)
{
MyThred.sendData("3");

}
else
{
MyThred.sendData("1");
}
}
}
sb.delete(0, sb.length());
break;
}
};
};

}


@Override
public void onResume() {
super.onResume();
if (btAdapter != null){
if (btAdapter.isEnabled()){
BluetoothDevice device = btAdapter.getRemoteDevice(MacAddress);
Log.d(LOG_TAG, "***Получили удаленный Device***"+device.getName());

try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
Log.d(LOG_TAG, "...Создали сокет...");
} catch (IOException e) {
MyError("Fatal Error", "В onResume() Не могу создать сокет: " + e.getMessage() + ".");
}

btAdapter.cancelDiscovery();
Log.d(LOG_TAG, "***Отменили поиск других устройств***");

Log.d(LOG_TAG, "***Соединяемся...***");
try {
btSocket.connect();
Log.d(LOG_TAG, "***Соединение успешно установлено***");
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
MyError("Fatal Error", "В onResume() не могу закрыть сокет" + e2.getMessage() + ".");
}
}

MyThred = new ConnectedThred(btSocket);
MyThred.start();
}
}
}

@Override
public void onPause() {
super.onPause();

Log.d(LOG_TAG, "...In onPause()...");

if (btAdapter != null){
if (btAdapter.isEnabled()){
if (MyThred.status_OutStrem() != null) {
MyThred.cancel();
}
try {
btSocket.close();
} catch (IOException e2) {
MyError("Fatal Error", "В onPause() Не могу закрыть сокет" + e2.getMessage() + ".");
}
}
}
}//OnPause

private void MyError(String title, String message){
Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
finish();
}


//Отдельный поток для передачи данных
private class ConnectedThred extends Thread{
private final BluetoothSocket copyBtSocket;
private final OutputStream OutStrem;
private final InputStream InStrem;

public ConnectedThred(BluetoothSocket socket){
copyBtSocket = socket;
OutputStream tmpOut = null;
InputStream tmpIn = null;
try{
tmpOut = socket.getOutputStream();
tmpIn = socket.getInputStream();
} catch (IOException e){}

OutStrem = tmpOut;
InStrem = tmpIn;
}

public void run()
{
byte[] buffer = new byte[1024];
int bytes;

while(true){
try{
bytes = InStrem.read(buffer);
h.obtainMessage(ArduinoData, bytes, -1, buffer).sendToTarget();
}catch(IOException e){break;}

}

}

public void sendData(String message) {
byte[] msgBuffer = message.getBytes();
Log.d(LOG_TAG, "***Отправляем данные: " + message + "***" );

try {
OutStrem.write(msgBuffer);
} catch (IOException e) {}
}

public void cancel(){
try {
copyBtSocket.close();
}catch(IOException e){}
}

public Object status_OutStrem(){
if (OutStrem == null){return null;
}else{return OutStrem;}
}
}
}





Созданное приложение для андроида в связке с представленным скетчем ардуино, позволяет, как удаленно самостоятельно управлять роботом, так и включать режим автономного управление, при котором робот передвигается в прямом направлении и если требуется, объезжает препятствия.

Результатом проделанной работы является простейший рефлексный робот. Дальнейшее применение более сложных алгоритмов на базе приведенных шаблонных приложений и скетчей позволит создавать роботов основанных на модели, на цели, на полезности, обучающихся роботов и др.

К следующей статье я сделал заказ всего одного модуля:




ИТОГ: 524,65

В комментариях к предыдущей статье, хабра пользователь commanderxo порекомендовал не изобретать велосипед, а воспользоваться стандартным протоколом Firmata (протокол для обмена данными между ардуино и сервером). К сожалению работоспособной библиотеки, для андроида в связке с БТ, я не нашел. Написать свою библиотеку у меня не хватает времени и сил, поэтому в данной статье я продолжаю изобретать велосипед. Если кто из Хабра пользователей обладает информацией о такой библиотеке просьба поделиться.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий