BOXZ mini/zh

来自Microduino Wikipedia
502748957@qq.com讨论 | 贡献2015年11月16日 (一) 11:31的版本 程序说明
跳转至: 导航搜索

概述

  • 项目名称:Microduino机器人小车
  • 目的:通过Microduino Joypad来控制BOXZ mini机器人小车
  • 难度:高级
  • 耗时:3小时
  • 制作者:Microduino Studio-YLB

材料清单

  • Microduino设备
模块 数量 功能
Microduino-Core/zh 1 核心板(Joypad)
Microduino-Core+/zh 1 核心板(Robot)
Microduino-USBTTL/zh 1 下载程序
Microduino-nRF24/zh 2 无线通讯
Microduino-Joypad/zh 1 遥控
Microduino-TFT/zh 1 显示
Microduino-Motor/zh 1 四轴电机驱动
Microduino-Robot/zh 1 驱动连接底板
  • 其他设备
模块 数量 功能
机器小车机架 1 车体
螺丝 18 固定
螺母 8 固定
Micro-USB数据线 1 下载程序
车轮 1 车体
电机 1 驱动车轮
电池 1 供电

实验原理

机器人小车种类比较多,如循迹,壁障,蓝牙遥控小车,电脑鼠等。但是其行走控制方式基本是一样的,无非就是前后左右四个方向运动。当然结构上会有一定区别,不同功能需要采用不同传感器,本次我们主要使用两轮驱动,通过控制两个轮子的旋转方向,实现前进后退,旋转等功能,当然还要加上万向轮,这样才能保持平衡。 该小车结构简单,主要包括三个方面:车轮、车身、控制系统。 1)车轮采用两个减速电机,扭力大,可PWN调速,控制简单。 2)车身采用亚克力板,大小:8cm*8cm*8cm。 3)整个控制系统包括四个部分:

  • 供电系统

因为BOXZ体积比较小,所以采用锂电池。

  • 中央处理器

中央处理器是整个小车的核心,就像电脑的CPU,人的大脑,有一定思维能力,能够处理复杂事件。采用Microduino-Core作为核心。

  • 无线通讯

小车采用Microduino-nRF24无线通讯方案,通讯速度响应快,控制范围:空阔地域大约100米。

  • 电机控制

采用Microduino-Motor直流电机驱动模块,一个模块能够驱动两个电机。同时结合Microduino-Robot底板,将中央处理器和直流电机模块连接起来。

文档

调试过程

将Microduino Core、Microduino USBTTL堆叠在一起.用数据线将写好的程序通过Microduino USBTTL上传到Microduino Core上。 注意:最好不要将所有模块堆叠在一起之后再上传程序

Download1.jpg

打开Aroduino IDE,若电脑中没有安装,则参照附录中的安装方法,先安装Aicroduino IDE。点击左上【文件】选项→点击【打开】。

浏览到项目程序地址,点击“Robot_v0.2.ino”程序打开

之后点击左上角的"√"进行编译,点击上边栏的工具,确认板卡(Microduino-Core)处理器(Atmega328P@16M,5V)和端口号(COMX)。三项都如图确认无误之后点击"→"按钮下载程序到开发板上

Joypad操作说明

  • 左上边是油门控制开关,打开(拨到上面),才能进行控制,你可以摇动摇杆,观察屏幕的变化。
  • 右边开关是精度调整开关,开关拨到上面可以最大幅度控制,否则只能小幅度控制了,小幅度有助于稳定控制。
  • 左边摇杆本次未使用。
  • 右边摇杆在垂直方向上控制前后方向移动,往上向前,往下向后,在水平方向上控制左右方向移动。

Joypad开机设置

打开遥控器电源开关,按下复位按键(左边USB接口右边那个)进入系统,请在4S内按下【key1】按键,进入遥控器校准和控制选择模式。 360度最大幅度旋转两个摇杆,遥控板会读入摇杆的位置数据,摇动至示数不再变化即可

选择控制模式,可以通过【key3】按键来选择是控制四轴飞行器(Quad.)还是机器人(Robot),Robot模式可控制自平衡车和BOXZ mini,黑色表示选中。因此我们需要选择Robot模式。还可以通过【key4】按键来选择是否是体感控制模式,如果选择体感模式,你必须叠加Microduino-10DOF模块,选择“MPU ON”。如果是摇杆控制模式,选择“MPU OFF”。 这次搭建没有使用10DOF模块,因此选择MPU OFF模式;

选择完成后,通过【key2】按键退出配置,进入操作

将左上边控制开关打开(拨到上面),才能进行控制,你可以摇动摇杆,观察屏幕的变化

右边开关是幅度调节模式,开关拨到上面可以最大幅度控制Robot,否则只能小幅度控制。如果使用小幅度控制小车,右边摇杆拨到最大位置,小车速度也只能小范围变化,这样有助于稳定控制

当启动小车时,只需要用到右边的摇杆,摇杆的方向和平衡车的方向一致,你可以尝试摇杆控制是否正确。如果发现方向有问题,可以在Robot_v0.2前4行代码更改引脚定义 如果原来的引脚定义如下:

而此时左右旋转反了,可以更改为

如果最开始的引脚是情况2,那么方向错了就改成情况1就可以了。 打开BOXZ_mini小车上Microduino-Robot底板上的电源开关,拨到ON(左边),如果可以看到核心板上的红色led亮,说明供电正常。你可以愉快的玩耍了。


注意问题

  • 下载程序时候最好只叠加core(core+)和USBTTL,虽然本次搭建涉及的nRF24不会引起冲突,但是别的通信模块有时会造成串口冲突,养成好习惯。
  • 锂电池正负极别接错了,否则会烧坏电路。
  • 调试好后,实际运行时不要使用USB供电,电压不足,请使用电池。

小车程序说明

#define motor_pin0A 7  //PWM
#define motor_pin0B 5
#define motor_pin1A 8  //PWM 
#define motor_pin1B 6

#define FIX_THROTTLE_A 1  //-1 or 1
#define FIX_THROTTLE_B -1  //-1 or 1

#define REVERSE_THROTTLE 1      //-1 or 1
#define REVERSE_STEERING 1     //-1 or 1

#define MAX_THROTTLE 255 //最大油门 100~255
#define MAX_STEERING 200 //最大转向 100~512

//rf=======================================
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>

// nRF24L01(+) radio attached using Getting Started board
RF24 radio(9, 10);
RF24Network network(radio);
const uint16_t this_node = 1;	//设置本机ID
const uint16_t other_node = 0;

//--------------------------------
struct send_a	//发送
{
  uint32_t node_ms;		//节点运行时间
};

unsigned long last_sent = 0;	//定时器

//--------------------------------
struct receive_a	//接收
{
  uint32_t ms;
  uint16_t rf_CH0;
  uint16_t rf_CH1;
  uint16_t rf_CH2;
  uint16_t rf_CH3;
  uint16_t rf_CH4;
  uint16_t rf_CH5;
  uint16_t rf_CH6;
  uint16_t rf_CH7;
};

unsigned long clock;	//主机运行时间
int tem_xuan = 0;			//主机请求时序

//----------------------------
boolean node_STA = false;
float throttle;
float steering;

unsigned long safe_ms = millis();

void setup()
{
  Serial.begin(115200);

  pinMode(motor_pin0A, OUTPUT);
  pinMode(motor_pin0B, OUTPUT);
  pinMode(motor_pin1A, OUTPUT);
  pinMode(motor_pin1B, OUTPUT);

  //nRF==============================
  SPI.begin();		//初始化SPI总线
  radio.begin();
  network.begin(/*channel*/ 70  , /*node address*/ this_node);

  Serial.println("===========start===========");
}


int motor_vol[2];
// 主循环//////////////////////////////////////////////////////////////////////////
void loop()
{
  //===============================================================
  if (nRF(&throttle, &steering))
  {
    vorobot();
    Serial.print(throttle);
    Serial.print(",");
    Serial.println(steering);
  }

  //===============================================================
  if (safe_ms > millis()) safe_ms = millis();
  if (millis() - safe_ms > 2000)
  {
    steering = 0;
    throttle = 0;

    digitalWrite(motor_pin0A, LOW);
    digitalWrite(motor_pin0B, LOW);
    digitalWrite(motor_pin1A, LOW);
    digitalWrite(motor_pin1B, LOW);
  }

}

void vorobot()
{
  /*
  if(node_STA)
   {

   }
   */

  //===============================================================
  int motor_speed = 0;
  motor_speed = REVERSE_THROTTLE * throttle;

  motor_vol[0] = motor_speed;
  motor_vol[1] = motor_speed;

  //----------------------------------
  int motor_steer = 0;
  motor_steer = REVERSE_STEERING * steering;

  motor_vol[0] -= motor_steer / 2;
  motor_vol[1] += motor_steer / 2;

  for (int a = 0; a < 2; a++)
  {
    if (motor_vol[a] > MAX_THROTTLE)
    {
      motor_vol[0] -= (motor_vol[a] - MAX_THROTTLE);
      motor_vol[1] -= (motor_vol[a] - MAX_THROTTLE);
    }
    else if (motor_vol[a] < -MAX_THROTTLE)
    {
      motor_vol[0] -= (MAX_THROTTLE + motor_vol[a]);
      motor_vol[1] -= (MAX_THROTTLE + motor_vol[a]);
    }
  }

  Serial.print(motor_vol[0]);
  Serial.print(",");
  Serial.print(motor_vol[1]);
  Serial.println("");

  motor_vol[0] *= FIX_THROTTLE_A;
  motor_vol[1] *= FIX_THROTTLE_B;

  motor_driver(0, -motor_vol[0]);
  motor_driver(1, motor_vol[1]);
}

boolean motor_driver(int _motor_driver_num, int _motor_driver_vol)
{
  switch (_motor_driver_num)
  {
    case 0:
      if (_motor_driver_vol == 0)
      {
        //Serial.println("0 OFF");
        digitalWrite(motor_pin0A, LOW);
        digitalWrite(motor_pin0B, LOW);
      }
      else if (_motor_driver_vol > 0)
      {
        //Serial.println("0 Z");
        analogWrite(motor_pin0A, _motor_driver_vol);
        digitalWrite(motor_pin0B, LOW);
      }
      else
      {
        //Serial.println("0 F");
        analogWrite(motor_pin0A, 255 + _motor_driver_vol);
        digitalWrite(motor_pin0B, HIGH);
      }
      break;
    case 1:
      if (_motor_driver_vol == 0)
      {
        //Serial.println("1 OFF");
        digitalWrite(motor_pin1A, LOW);
        digitalWrite(motor_pin1B, LOW);
      }
      else if (_motor_driver_vol > 0)
      {
        //Serial.println("1 Z");
        analogWrite(motor_pin1A, _motor_driver_vol);
        digitalWrite(motor_pin1B, LOW);
      }
      else
      {
        //Serial.println("1 F");
        analogWrite(motor_pin1A, 255 + _motor_driver_vol);
        digitalWrite(motor_pin1B, HIGH);
      }
      break;
    default :
      return false;
  }
  return true;
}


boolean nRF(float * _speed, float * _turn)
{
  network.update();
  // Is there anything ready for us?
  while ( network.available() )
  {
    // If so, grab it and print it out
    RF24NetworkHeader header;
    receive_a rec;
    network.read(header, &rec, sizeof(rec));

    clock = rec.ms;    //接收主机运行时间赋值
    float * _i = _speed;
    _i[0] = map(rec.rf_CH1, 1000, 2000, -MAX_THROTTLE, MAX_THROTTLE);
    _i = _turn;
    _i[0] = map(rec.rf_CH0, 1000, 2000, -MAX_STEERING, MAX_STEERING);

    node_STA = (rec.rf_CH7 > 1500 ? true : false);    //接收请求时序赋值

    {
      //Serial.print("Sending...");
      send_a sen = {
        millis()
      };  //把这些数据发送出去,对应前面的发送数组

      RF24NetworkHeader header(0);
      boolean ok = network.write(header, &sen, sizeof(sen));
      safe_ms = millis();
      if (ok)
      {
        return true;
        //Serial.println("ok.");
      }
      else
      {
        return false;
        //Serial.println("failed.");
      }
    }

    safe_ms = millis();
  }
}

Joypad程序说明

#include "Arduino.h"
#include "def.h"
#include "time.h"
#include "bat.h"
#if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega128RFA1__)
#include "mpu.h"
#endif
#include "joy.h"
#include "key.h"
#include "data.h"
#include "nrf.h"
#include "mwc.h"
#include "tft.h"
#include "eep.h"

#if defined(__AVR_ATmega128RFA1__)
#include <ZigduinoRadio.h>
#endif

//joypad================================
#include <Joypad.h>
//eeprom================================
#include <EEPROM.h>
//TFT===================================
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific 
#include <SPI.h>
//rf====================================
#include <RF24Network.h>
#include <RF24.h>

#if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega128RFA1__)
//MPU===================================
#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#endif

//spi===================================
#include <SPI.h>

void setup()
{
  // initialize serial communication at 115200 bits per second:

#ifdef Serial_DEBUG
  Serial.begin(115200);
  delay(100);
  Serial.println("========hello========");
#endif

  //---------------
  key_init();

  //---------------
#ifdef Serial_DEBUG
  Serial.println("\n\r EEPROM READ...");
#endif
  eeprom_read();

  //---------------
#ifdef Serial_DEBUG
  Serial.println("\n\r TFT INIT...");
#endif
  TFT_init(true, tft_rotation);

  //---------------
#ifdef Serial_DEBUG
  Serial.println("\n\r TFT BEGIN...");
#endif
  TIME1 = millis();
  while (millis() - TIME1 < interval_TIME1)
  {
    TFT_begin();

    if (!Joypad.readButton(CH_SWITCH_1))
    {
#ifdef Serial_DEBUG
      Serial.println("\n\rCorrect IN...");
#endif

      //---------------
#ifdef Serial_DEBUG
      Serial.println("\n\r TFT INIT...");
#endif
      TFT_init(false, tft_rotation);

      while (1)
      {
        if (!TFT_config())
          break;
      }
#ifdef Serial_DEBUG
      Serial.println("\n\rCorrect OUT...");
#endif

      //---------------
#ifdef Serial_DEBUG
      Serial.println("\n\r EEPROM WRITE...");
#endif
      eeprom_write();
    }
  }

  //---------------
#ifdef Serial_DEBUG
  Serial.println("\n\r TFT CLEAR...");
#endif
  TFT_clear();

  //---------------
#ifdef Serial_DEBUG
  Serial.println("\n\r TFT READY...");
#endif
  TFT_ready();

  //---------------.l
  if (mode_protocol)   //Robot
  {
    SPI.begin();		//初始化SPI总线
    radio.begin();
    network.begin(/*channel*/ nrf_channal, /*node address*/ this_node);
  }
  else          //QuadCopter
  {
    unsigned long _channel;
#if !defined(__AVR_ATmega128RFA1__)
    switch (mwc_channal)
    {
      case 0:
        _channel = 9600;
        break;
      case 1:
        _channel = 19200;
        break;
      case 2:
        _channel = 38400;
        break;
      case 3:
        _channel = 57600;
        break;
      case 4:
        _channel = 115200;
        break;
    }
#else if
    _channel = mwc_channal;
#endif
    mwc_port.begin(_channel);
  }

  //---------------
#ifdef Serial_DEBUG
  Serial.println("===========start===========");
#endif

#if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega128RFA1__)
  if (mode_mpu) initMPU(); //initialize device
#endif
}

void loop()
{
  //  unsigned long time = millis();

#if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega128RFA1__)
  //MPU--------------------------------
  if (mode_mpu)
    getMPU();
#endif

  //DATA_begin------------------------------
  data_begin();

  //DATA_send-------------------------------
  if (millis() < time2) time2 = millis();
  if (millis() - time2 > interval_time2)
  {
    if (mode_protocol) nrf_send();    //Robot
    else data_send();           //QuadCopter

    time2 = millis();
  }

  //节点查错-------------------------------
  vodebug();

  //BAT--------------------------------
  if (time3 > millis()) time3 = millis();
  if (millis() - time3 > interval_time3)
  {
    vobat();
    time3 = millis();
  }

  //TFT------------------------------------
  TFT_run();

  //===================================
  //  time = millis() - time;

  //  Serial.println(time, DEC);    //loop time
}

视频