概述

智能硬件做了很久了,但是一直没有真正火起来,直到这一波移动互联网浪潮的推动。 我们做智能硬件也应考虑结合嵌入式和互联网技术。

做一款语音控制的自平衡车,结合了自平衡技术和百度语音识别技术,玩起来一定很酷。 驾驶员只需要对车说话,车就能按你的旨意走。或许这也会是对残疾人开车的一个福音。

演示视频

设计

总体设计

用户对手机说命令,手机根据用户的指令控制车。

自平衡车设计

自平衡基本原理,从陀螺仪读取数据计算车当前角度,调整汽车速度,使车身稳定。基本原理:

模拟控制有误差,需要PID(比例积分微分)控制。

工作流程如下:

手机控制器设计

手机把主人的语音用百度服务识别成文字后,分析文字内容,生成指令,发给自平衡车。

控制协议设计

手机和车用蓝牙连接,协议宜尽量简单,把更多的控制交给手机。设计了如下命令:

‘S’: vehicle stop and stand

‘F’: vehicle go ahead

‘B’: vehicle go back

‘L’: vehicle turn left

‘R’: vehicle turn right

‘A’: vehicle accelerate if is running

‘D’: vehicle sit down

‘U’: vehicle stand up

把运行的参数作为手机命令的计时,让命令尽量简单,单向。

制作

制作首先需准备工具:

材料需求

1. Arduino Leonardo主控板 * 1

2. 车轮 * 2

3. 步进电机 * 2

4. 电机驱动模块 * 2

5. 陀螺仪模块 * 1

6. 蓝牙模块 * 1

7. 板材 若干

8. 螺丝螺帽 若干

硬件拼装

1. 制作地板:用板材裁剪一块底板,形状执行设计,打些孔用于穿线。参考如下:

2. 把电机固定地板上:

3. 把轮子,电池安上:

4. 拼装控制板:

5. 把控制板安上:

6. 接线,盖上顶棚,完成。

自平衡车Arduino编码

    #include 
    #include 
    #include   // 与DMP工作库的修改版本(见注释内)
    #define DEBUG 0
    #define CLR(x,y) (x&=(~(1<= period_m[0][period_m_index[0]])
    {
        counter_m[0] = 0;
        if (period_m[0][0]==ZERO_SPEED)
        return;
        if (dir_m[0])
        SET(PORTB,4);  //DIR电机1
        else
        CLR(PORTB,4);
        // 我们需要等待,以免200ns的产生步进脉冲...
        period_m_index[0] = (period_m_index[0]+1)&0x07; // 周期M指数从0到7
        //delay_200ns();
        SET(PORTD,7); // 步进电机1
        delayMicroseconds(1);
        CLR(PORTD,7);
    }
    if (counter_m[1] >= period_m[1][period_m_index[1]])
    {
        counter_m[1] = 0;
        if (period_m[1][0]==ZERO_SPEED)
        return;
        if (dir_m[1])
        SET(PORTC,7);   // DIR电机2
        else
        CLR(PORTC,7);
        period_m_index[1] = (period_m_index[1]+1)&0x07;
        //delay_200ns();
        SET(PORTD,6); // 步进电机1
        delayMicroseconds(1);
        CLR(PORTD,6);
    }
    }
    void calculateSubperiods(uint8_t motor)
    {
    int subperiod;
    int absSpeed;
    uint8_t j;

    if (speed_m[motor] == 0)
    {
        for (j=0;j<8;j++)
        period_m[motor][j] = ZERO_SPEED;
        return;
    }
    if (speed_m[motor] > 0 )   // 正速度
    {
        dir_m[motor] = 1;
        absSpeed = speed_m[motor];
    }
    else                       // 负速度
    {
        dir_m[motor] = 0;
        absSpeed = -speed_m[motor];
    }

    for (j=0;j<8;j++)
        period_m[motor][j] = 1000/absSpeed;
        // 计算亚期。如果模块<0.25=>子期间= 0,如果模块<0.5=>子周期= 1。如果模块<0.75子期间=2其他子期间=3
        subperiod = ((1000 % absSpeed)*8)/absSpeed;   // 优化代码来计算子期间(整数运算)
        if (subperiod>0)
        period_m[motor][1]++;
        if (subperiod>1)
        period_m[motor][5]++;
        if (subperiod>2)
        period_m[motor][3]++;
        if (subperiod>3)
        period_m[motor][7]++;
        if (subperiod>4)
        period_m[motor][0]++;
        if (subperiod>5)
        period_m[motor][4]++;
        if (subperiod>6)
        period_m[motor][2]++;
    }
    void setMotorSpeed(uint8_t motor, int16_t tspeed)
    {
        // 我们限制最大加速度
        if ((speed_m[motor] - tspeed)>MAX_ACCEL)
        speed_m[motor] -= MAX_ACCEL;
        else if ((speed_m[motor] - tspeed)<-MAX_ACCEL)
        speed_m[motor] += MAX_ACCEL;
        else
        speed_m[motor] = tspeed;
        calculateSubperiods(motor);  //我们采用四个子周期来提高分辨率
        // 为了节省能量,当它没有运行...
        if ((speed_m[0]==0)&&(speed_m[1]==0))
        digitalWrite(4,HIGH);   //disable motor
        else
        digitalWrite(4,LOW);   //enable motor
    }
    void setup()
    {
        VhcState = VEHICLE_STATE_INITING;
        //Serial.begin(9600);
        //Serial.println("begin setup()");
        // 步进引脚
        pinMode(4,OUTPUT);  // ENABLE MOTORS
        pinMode(12,OUTPUT);  // STEP MOTOR 1 PORTD,7//6
        pinMode(13,OUTPUT);  // DIR MOTOR 1 8
        pinMode(6,OUTPUT); // STEP MOTOR 2 PORTD,6 12
        pinMode(8,OUTPUT); // DIR MOTOR 2 13
        digitalWrite(4,HIGH);   // Disbale motors
        Serial1.begin(9600);
        // 加入I2C总线
        Wire.begin();
        // 4000Khz fast mode
        TWSR = 0;
        TWBR = ((16000000L/I2C_SPEED)-16)/2;
        TWCR = 1<=18)
    {

        if (fifoCount>18)  // 如果我们有一个以上的数据包,我们采取简单的路径:丢弃缓冲区
        {
        mpu.resetFIFO();
        return;
    }

    loop_counter++;
    slow_loop_counter++;
    dt = (timer_value-timer_old);
    timer_old = timer_value;
    angle_adjusted_Old = angle_adjusted;
    angle_adjusted = dmpGetPhi();
    mpu.resetFIFO();  // 我们始终复位FIFO
    // 我们计算估计机器人的速度
    actual_robot_speed_Old = actual_robot_speed;
    actual_robot_speed = (speed_m[1] - speed_m[0])/2;  // 正面:前锋
    // 角速度角度调整角度调整旧
    int16_t angular_velocity = (angle_adjusted-angle_adjusted_Old)*90.0;
    // 我们利用机器人速度(T-1)或(T-2),以补偿该延迟
    int16_t estimated_speed = actual_robot_speed_Old - angular_velocity;
    //估计速度估计过滤滤速
    estimated_speed_filtered = estimated_speed_filtered*0.95 + (float)estimated_speed*0.05;
    //目标角速度PIC ONTROL dt的速度估计过滤油门Kp_thr Ki_thr
    target_angle = speedPIControl(dt,estimated_speed_filtered,throttle,Kp_thr,Ki_thr);
    //有限的输出  目标角度约束目标角度最大目标角度最大目标角度
    target_angle = constrain(target_angle,-max_target_angle,max_target_angle);
    //我们整合输出(加速度)
    control_output += stabilityPDControl(dt,angle_adjusted,target_angle,Kp,Kd);
    control_output = constrain(control_output,-800,800);   // 限制最大输出控制
    // 控制的转向部分的输出直接注射
    motor1 = control_output + steering;
    motor2 = -control_output + steering;   //马达2反转
    // 限制最大速度
    motor1 = constrain(motor1,-max_throttle,max_throttle);
    motor2 = constrain(motor2,-max_throttle,max_throttle);
    setMotorSpeed(0,motor1);
    setMotorSpeed(1,motor2);
    }
    }
    void Controller()
    {
    lkf = 0;
    lkf = Serial1.read();
    switch(lkf)
    {
        case 'A':
        if( 0 < throttle)
        {
        if(throttle <  SpeedMax)
        throttle = throttle*2;
        }
        else if(0 > throttle)
        {
        if(-throttle <	SpeedMax)
        throttle = throttle*2;
        }
        break;
        case 'F':
        throttle = -SpeedStep;
        break;
        case 'B':
        throttle = SpeedStep;
        break;
        case 'R':
        steering = -80;
        break;
        case 'L':
        steering = 80;
        break;
        case 'S':
        throttle = 0;
        steering =0;
        break;
        case 'D':
        VhcState = VEHICLE_STATE_RESTING;
        digitalWrite(4,HIGH);   //disable motor
        break;
        case 'U':
        VhcState = VEHICLE_STATE_RUNNING;
        digitalWrite(4,LOW);   //enable motor
        break;
    }
    }
                     

手机控制器安卓app编码

见下载资料中controller

测试

测试步骤

1. 测试车子,单体自平衡,手动干扰后再平衡;

2. 测试手机与车子蓝牙连接;

3. 测试手机手动发送控制命令,车子执行情况;

4. 测试手机语音识别,文字返回准确率;

5. 测试手机文字分析准确率;

6. 测试整体,手机语音控制自平衡车。

资料下载

资料清单