树莓派论坛

 找回密码
 立即注册

树莓派WIFI小车java版(三)小车java控制程序(续1)

seth.yang 发表于 2015-7-1 16:33:53 | 显示全部楼层 |阅读模式
由于帖子字数实在太多,只能拆出来发贴,接连

-- 接上一节内容 (http://www.shumeipai.net/thread-26817-1-1.html)
直流电机控制
直流电机是通过L298N控制板进行控制的。一块L298N控制板可以同时控制2路直流电机,其中,每路直流电机需要3根脚进行控制,一根使用PWM信号控制电机的转速,2路数字信号控制电机的转动状态,请参见下表
wire-4.png
了解L298N的控制方式后,控制代码比较简单。
  1. package org.dreamwork.smart.car.server.component;
  2. public class Motor {
  3.     private GpioPinDigitalOutput pin0, pin1;
  4.     private PWM pwm;
  5.     private int speed = 3;

  6.     public static final int MAX_SPEED = 5, MIN_SPEED = 0;

  7.     public Motor (GpioPinDigitalOutput pin0, GpioPinDigitalOutput pin1, PWM pwm) {
  8.         this.pin0 = pin0;
  9.         this.pin1 = pin1;
  10.         this.pwm = pwm;
  11.     }

  12.     public int getSpeed () {
  13.         return speed;
  14.     }

  15.     public void setSpeed (int speed) {
  16.         if (speed < MIN_SPEED) speed = MIN_SPEED;
  17.         if (speed > MAX_SPEED) speed = MAX_SPEED;
  18.         this.speed = speed;
  19.                 // 将速度映射为PWM占空比
  20.         pwm.setValue (.2f * speed);
  21.     }

  22.     public void forward () {
  23.         pin0.high ();
  24.         pin1.low ();
  25.     }

  26.     public void backward () {
  27.         pin0.low ();
  28.         pin1.high ();
  29.     }

  30.     public void stop () {
  31.         pin0.low ();
  32.         pin1.low ();
  33.     }

  34.     public void dispose () {
  35.         pin0.low ();
  36.         pin1.low ();
  37.         pwm.setValue (0);
  38.     }
  39. }
复制代码


增加转向灯
这部分对于小车运动控制来说,不是必须的,但可以增加趣味性,同时更加接近现实情况:当车辆转弯时转向灯同时闪烁,指示车辆转弯的方向。
这部分的代码相当简单,就是控制一个LED灯的闪烁,唯一不同的事,采用线程,使得LED的闪烁不影响主线程(小车控制命令接收线程)
  1. package org.dreamwork.smart.car.server.component;

  2. import com.pi4j.io.gpio.GpioPinDigitalOutput;
  3. import org.apache.log4j.Logger;
  4. import org.dreamwork.smart.car.server.util.GpioHelper;
  5. import org.dreamwork.smart.car.server.util.PausableThread;

  6. /**
  7. * Created by seth.yang on 2015/6/8.
  8. */
  9. public class BlinkLED extends PausableThread {
  10.     private GpioPinDigitalOutput pin;

  11.     public BlinkLED (int pinIndex) {
  12.         super (true);
  13.         pin = GpioHelper.getDigitalOutputPin (pinIndex);
  14.     }

  15.     public boolean isBlinking () {
  16.         return !paused;
  17.     }

  18.     public void blink () {
  19.         proceed ();
  20.     }

  21.     @Override
  22.     protected void doWork () {
  23.         try {
  24.             pin.high ();
  25.             sleep (300);
  26.             pin.low ();
  27.             sleep (300);
  28.         } catch (Exception ex) {
  29.                         // process exception
  30.         }
  31.     }
  32. }
复制代码

摄像头
这个和java关系不大,和GPIO也没关系。通过java调用mjpg-streamer就行了。

拼装小车控制程序

小车动作控制部分介绍完了,可以将这些代码拼装成一台完整的小车了。
我们的小车需要:
l 两路舵机,用于控制摄像头的转动
l 四路直流电机(实际上我只用了两路,记得吗,我将左右两侧的2个电机并联了)
l 四路转向灯(实际上是也是两路,同上)
l 一路照明LED灯(好吧,上面的介绍中没有,但这不影响整体代码)
  1. package org.dreamwork.smart.car.server.component;

  2. import com.pi4j.io.gpio.GpioController;
  3. import com.pi4j.io.gpio.GpioFactory;
  4. import com.pi4j.io.gpio.GpioPinDigitalOutput;
  5. import org.apache.log4j.Logger;
  6. import org.dreamwork.smart.car.server.util.Config;
  7. import org.dreamwork.smart.car.server.util.GpioHelper;
  8. import org.dreamwork.smart.car.server.util.Rotate;

  9. import java.io.*;

  10. public class Car implements ServoListener {
  11.     private static final int
  12.             DIR_FORWARD = 1, DIR_BACKWARD = -1, DIR_STOP = 0,
  13.             DIR_TURN_LEFT = -2, DIR_TURN_RIGHT = 2;

  14.         /**
  15.          * 小车当前运动状态(前进,后退或停止)
  16.          */
  17.     private int dir = DIR_STOP;
  18.         /**
  19.          * 小车上一次的运动状态
  20.          */
  21.     private Integer backup_dir = null;
  22.         /**
  23.          * 小车转动状态(左转,右转,停止)
  24.          */
  25.     private Rotate rotate;
  26.         /**
  27.          * 两路转向灯
  28.          */
  29.     private BlinkLED leftLed, rightLed;
  30.         /**
  31.          * 两路舵机
  32.          */
  33.     private Servo servo0, servo1;
  34.         /**
  35.          * 两路直流电机
  36.          */
  37.     private Motor left_front, right_front;
  38.         /**
  39.          * 摄像头
  40.          */
  41.     private Camera camera;
  42.         /**
  43.          * 配置文件
  44.          */
  45.     private Config config;
  46.         /**
  47.          * 前灯
  48.          */
  49.     private GpioPinDigitalOutput led;

  50.     private GpioController gpio;
  51.     private boolean shutdown = false;

  52.     public Car (Config config) throws IOException, InterruptedException {
  53.         this.config = config;
  54.         setup ();
  55.     }

  56.         /**
  57.          * 初始化
  58.          */
  59.     private void setup () throws IOException, InterruptedException {
  60.                 ... // 读取配置文件,初始化各个部件
  61.     }

  62.         /**
  63.          * 使指定的LED灯闪烁
  64.          */
  65.     private void toggleBlinkLed (BlinkLED led) {
  66.                 ...
  67.     }

  68.         /**
  69.          * 重置小车的各个状态
  70.          */
  71.     public void reset () throws InterruptedException {
  72.         stop ();
  73.         servo0.reset ();
  74.         servo1.reset ();
  75.         camera.close ();
  76.     }

  77.         /**
  78.          * 销毁小车的各个部件,释放资源
  79.          */
  80.     public void dispose () throws InterruptedException {
  81.                 ...
  82.     }

  83.     public void toggleLeftBlink () {
  84.         ...
  85.     }

  86.     public void toggleRightBlink () {
  87.         ...
  88.     }

  89.     public void forward () throws InterruptedException {
  90.                 ...
  91.     }

  92.     public void backward () throws InterruptedException {
  93.                 ...
  94.     }

  95.     public void stop () throws InterruptedException {
  96.                 ...
  97.     }

  98.     public void toggleServoLeft () {
  99.                 ...
  100.     }

  101.     public void toggleServoRight () {
  102.                 ...
  103.     }

  104.     public void toggleServoUp () {
  105.                 ...
  106.     }

  107.     public void toggleServoDown () {
  108.                 ...
  109.     }

  110.     public void toggleTurnLeft () throws InterruptedException {
  111.                 ...
  112.     }

  113.     public void toggleTurnRight () throws InterruptedException {
  114.                 ...
  115.     }

  116.     public void toggleLED () {
  117.         led.toggle ();
  118.     }

  119.     public void toggleCamera () throws IOException {
  120.         if (!camera.isOpened ())
  121.             camera.open ();
  122.         else
  123.             camera.close ();
  124.     }

  125.     public void servoLeft () {
  126.         servo0.increase ();
  127.     }

  128.     public void servoRight () {
  129.         servo0.decrease ();
  130.     }

  131.     public void stopHorizontalRotate () {
  132.         servo0.stopRotate ();
  133.     }

  134.     public void servoUp () {
  135.         servo1.decrease ();
  136.     }

  137.     public void servoDown () {
  138.         servo1.increase ();
  139.     }

  140.     public void stopVerticalRotate () {
  141.         servo1.stopRotate ();
  142.     }

  143.     public boolean isRotateUp () {
  144.         return rotate.isRotateUp ();
  145.     }

  146.     public boolean isRotateLeft () {
  147.         return rotate.isRotateLeft ();
  148.     }

  149.     public boolean isRotateDown () {
  150.         return rotate.isRotateDown ();
  151.     }

  152.     public boolean isRotateRight () {
  153.         return rotate.isRotateRight ();
  154.     }
  155. }
复制代码
代码较长,抱歉没有更多的注释,不过我想看方法名应该差不多就能理解方法是干什么的了
值得一提的是控制转向。我们的小车是四驱的,并没有转向轮,那么如何转向呢?答案是差速转向:当左右两侧轮子的转速不同时,小车就不再沿直线运动了。
设小车左轮的速度为VL,右轮的转速为VR,那么理论上:
n 当VL = VR时,转弯半径为无穷大(直线)
n 当VL > VR>0时,小车右转,转弯半径 > 轴距
n 当VL > VR=0时,小车右转,转弯半径 = 轴距
n 当 VR = -VL时,小车右转,转弯半径 = 0
反之左转
我们的转弯代码取其中一种(我取的是一侧速率为0的方式,这取决于电机的减速比和轮胎的抓地力,如果电机减速比较小或轮胎抓地力较小,建议取VR = -VL,进行原地转弯)
  1. ...
  2. // 一侧速率为0的转弯方式
  3. private void turnLeft () throws InterruptedException {
  4.         left_front.setSpeed (5);
  5.         right_front.setSpeed (5);
  6.         if (dir == DIR_BACKWARD) {
  7.                 left_forward ();
  8.                 right_pause ();
  9.         
  10.         } else {
  11.                 left_pause ();
  12.                 right_forward ();
  13.         }
  14.         leftLed.blink ();
  15. }

  16. // 原地转弯的方式
  17. private void turnLeft () throws InterruptedException {
  18.         left_forward ();
  19.     right_backward ();
  20.         leftLed.blink ();
  21. }
  22. ...
复制代码


指令
我们需要指令,来和小车的动作一一对应,枚举它就行了

  1. package org.dreamwork.smart.car.server.io;

  2. public enum Command {
  3.     DISPOSE (-2, false),
  4.     QUIT (-1, false),

  5.     STOP ( 0, false),
  6.     FORWARD (1, false),
  7.     BACKWARD (2, false),
  8.     TURN_LEFT (3, false),
  9.     TURN_RIGHT (4, false),

  10.     TOGGLE_LED (5, false),
  11.     TOGGLE_CAMERA (6, false),
  12.     TOGGLE_LEFT_BLINK (7, false),
  13.     TOGGLE_RIGHT_BLINK (8, false),

  14.     STOP_VERTICAL_SERVO (9, false),
  15.     STOP_HORIZONTAL_SERVO (10, false),

  16.     SPEED (11, false),
  17.     SERVO_UP (12, false),
  18.     SERVO_RIGHT (13, false),
  19.     SERVO_DOWN (14, false),
  20.     SERVO_LEFT (15, false),

  21.     LEFT_FORWARD (101, false),
  22.     LEFT_BACKWARD (102, false),
  23.     LEFT_PAUSE (103, false),
  24.     RIGHT_FORWARD (104, false),
  25.     RIGHT_BACKWARD (105, false),
  26.     RIGHT_PAUSE (106, false),

  27.     RESET (501, false)
  28.     ;

  29.     public final int code;
  30.     public final boolean hasReturn;

  31.     private Command (int code, boolean hasReturn) {
  32.         this.code = code;
  33.         this.hasReturn = hasReturn;
  34.     }
  35. }
复制代码


-- 还是写不下所有内容,只能再次拆楼。预告剩余内容包含:

  • 网络监听部分
  • 基于swing的测试前端
  • 成品效果图和视频


不要走开哦




您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版 | Archiver | 树莓派论坛 ( 粤ICP备15075382号-1 )