基于树莓派的遥控小车 您所在的位置:网站首页 基于树莓派的智能小车设计 基于树莓派的遥控小车

基于树莓派的遥控小车

2024-06-11 18:30| 来源: 网络整理| 查看: 265

一、引言

最近在准备一个计算机类比赛,需要用树莓派做成一个智能乒乓球捡球车,做过一些人工智能项目的也知道,所谓人工智能,只有人工,没有智能(实力吐槽),只要你知道实现的原理,做很多人工智能的东西都能得心应手。在准备比赛之际遇到瓶颈,停了下来做了一个简单的遥控小车。(老实说,智能捡球车还没有我这个遥控车好玩)接下来进入正题:

利用树莓派等相关硬件,制作安卓端控制遥控小车。通过安卓手机与树莓派小车建立网络连接,自由控制小车的运动。

二、准备工作

1.安卓手机一部

2.树莓派小车

3.网线

三、效果预览

         

ps:左图为我的树莓派小车,外形可以随意,其他树莓派小车同样适用该教程,主要区别在于我的树莓派只有两个电机驱动,其他小车可能会有四个电机驱动,代码实现中需要再加另外两个电机的驱动代码,这个没多大关系,会操纵两个电机同样的道理,就算来多少个电机也能随意操纵。右图是我的手机端界面,本来就做的丑,被压缩过的图片放上去更丑,这个无所谓,功能可以完美实现。

四、功能实现

1.建立连接:

利用网线连接树莓派3B+主板与电脑端,通过putty工具进行SSH连接,然后设置WIFI,将手机与树莓派连接,具体操作方法可以看我的另一篇博客:https://blog.csdn.net/qq_42109746/article/details/88537284

2.实现原理:安卓手机与树莓派之间通信主要是通过Socket套接字实现,在树莓派端运行Python程序,开启端口进行监听,安卓端向树莓派发送消息,树莓派端通过端口接收到安卓端发过来的消息,执行相应的动作。

3.树莓派端程序:

# coding:utf-8 import RPi.GPIO as GPIO import time import socket import sys HOST_IP = "192.168.43.212" # 树莓派IP地址 HOST_PORT = 7654 # 端口号 #########定义模式############ GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) ########电机驱动接口定义############ ENA = 13 # //L298使能A ENB = 20 # //L298使能B IN1 = 16 # //电机接口1 IN2 = 19 # //电机接口2 IN3 = 21 # //电机接口3 IN4 = 26 # //电机接口4 #########电机初始化为LOW########## GPIO.setup(ENA, GPIO.OUT, initial=GPIO.LOW) GPIO.setup(IN1, GPIO.OUT, initial=GPIO.LOW) GPIO.setup(IN2, GPIO.OUT, initial=GPIO.LOW) GPIO.setup(ENB, GPIO.OUT, initial=GPIO.LOW) GPIO.setup(IN3, GPIO.OUT, initial=GPIO.LOW) GPIO.setup(IN4, GPIO.OUT, initial=GPIO.LOW) ########定义左右电机初始速度######### pwm1 = GPIO.PWM(ENA, 80) # 第一个参数为引脚号,第二个参数为频率(HZ) pwm2 = GPIO.PWM(ENB, 80) pwm1.start(60) # 以百分比设置速度 pwm2.start(60) #########定义电机正转函数########## def forward(): stop() print('正在前进....') GPIO.output(ENA, True) GPIO.output(IN1, True) GPIO.output(IN2, False) GPIO.output(ENB, True) GPIO.output(IN3, True) GPIO.output(IN4, False) #########定义电机反转函数########## def back(): stop() print('正在后退....') GPIO.output(ENA, True) GPIO.output(IN1, False) GPIO.output(IN2, True) GPIO.output(ENB, True) GPIO.output(IN3, False) GPIO.output(IN4, True) #########定义电机停止函数########## def stop(): print('停止....') GPIO.output(ENA, False) GPIO.output(ENB, False) GPIO.output(IN1, False) GPIO.output(IN2, False) GPIO.output(IN3, False) GPIO.output(IN4, False) ########左转弯函数####### def left(): stop() print('正在左转....') GPIO.output(ENA, True) GPIO.output(IN1, True) GPIO.output(IN2, False) GPIO.output(ENB, True) GPIO.output(IN3, False) GPIO.output(IN4, True) ########左转弯函数####### def right(): stop() print('正在右转....') GPIO.output(ENA, True) GPIO.output(IN1, False) GPIO.output(IN2, True) GPIO.output(ENB, True) GPIO.output(IN3, True) GPIO.output(IN4, False) # pwm1.ChangeDutyCycle(100) # pwm2.ChangeDutyCycle(100) print("Starting socket: TCP...") socket_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建socket print("TCP server listen @ %s:%d!" % (HOST_IP, HOST_PORT)) host_addr = (HOST_IP, HOST_PORT) socket_tcp.bind(host_addr) # 绑定我的树莓派的ip地址和端口号 socket_tcp.listen(1) # listen函数的参数是监听客户端的个数,这里只监听一个,即只允许与一个客户端创建连接 while True: print('waiting for connection...') socket_con, (client_ip, client_port) = socket_tcp.accept() # 接收客户端的请求 print("Connection accepted from %s." % client_ip) info = "已连接" socket_con.send(bytes(info, encoding="utf8")) # 发送数据 while True: data = str(socket_con.recv(1024), encoding="utf8") # 接收数据 if data == "1": forward() if data == "2": back() if data == "3": left() if data == "4": right() if data == "5": stop() socket_tcp.close()

4.安卓端程序:

AndroidManifest.xml:

activity_main.xml:

Rudder.java(自定义摇杆组件):

package com.example.chen.control; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PorterDuff.Mode; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.SurfaceHolder.Callback; public class Rudder extends SurfaceView implements Runnable,Callback{ private SurfaceHolder mHolder; private boolean isStop = false; private Thread mThread; private Paint mPaint; private Point mRockerPosition; //摇杆位置 private Point mCtrlPoint = new Point(280,280);//摇杆起始位置 private int mRudderRadius = 80;//摇杆半径 private int mWheelRadius = 200;//摇杆活动范围半径 private RudderListener listener = null; //事件回调接口 public static final int ACTION_RUDDER = 1 ; public Rudder(Context context) { super(context); } public Rudder(Context context, AttributeSet as) { super(context, as); this.setKeepScreenOn(true); mHolder = getHolder(); mHolder.addCallback(this); mThread = new Thread(this); mPaint = new Paint(); mPaint.setColor(Color.GREEN); mPaint.setAntiAlias(true);//抗锯齿 mRockerPosition = new Point(mCtrlPoint); setFocusable(true); setFocusableInTouchMode(true); setZOrderOnTop(true); mHolder.setFormat(PixelFormat.TRANSPARENT);//设置背景透明 } //设置回调接口 public void setRudderListener(RudderListener rockerListener) { listener = rockerListener; } @Override public void run() { Canvas canvas = null; while(!isStop) { try { canvas = mHolder.lockCanvas(); canvas.drawColor(Color.TRANSPARENT,Mode.CLEAR);//清除屏幕 mPaint.setColor(Color.CYAN); canvas.drawCircle(mCtrlPoint.x, mCtrlPoint.y, mWheelRadius, mPaint);//绘制范围 mPaint.setColor(Color.RED); canvas.drawCircle(mRockerPosition.x, mRockerPosition.y, mRudderRadius, mPaint);//绘制摇杆 } catch (Exception e) { e.printStackTrace(); } finally { if(canvas != null) { mHolder.unlockCanvasAndPost(canvas); } } try { Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { mThread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { isStop = true; } @Override public boolean onTouchEvent(MotionEvent event) { int len = MathUtils.getLength(mCtrlPoint.x, mCtrlPoint.y, event.getX(), event.getY()); if(event.getAction() == MotionEvent.ACTION_DOWN) { //如果屏幕接触点不在摇杆挥动范围内,则不处理 if(len >mWheelRadius) { return true; } } if(event.getAction() == MotionEvent.ACTION_MOVE){ if(len = Build.VERSION_CODES.KITKAT) { Window window = getWindow(); window.setFlags( WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } init(); new Thread(new Runnable() { @Override public void run() { try { socket = new Socket("192.168.43.212", 7654); if (socket != null) { while (true) { //循环进行收发 recv(); Thread.sleep(50); } } else System.out.println("socket is null"); } catch (Exception e) { e.printStackTrace(); } } }).start(); rudder.setRudderListener(new Rudder.RudderListener() { @Override public void onSteeringWheelChanged(int action, final int angle) { if (action == Rudder.ACTION_RUDDER) { handler.post(new Runnable() { @Override public void run() { tv.setText(String.valueOf("角度值:" + angle)); } }); if ((angle > 315 && angle < 360) || (angle > 0 && angle < 45)) {//右转 new Thread(new Runnable() { @Override public void run() { send_buff = right; OutputStream outputStream = null; try { outputStream = socket.getOutputStream(); } catch (IOException e) { e.printStackTrace(); } if (outputStream != null) { try { outputStream.write(send_buff.getBytes()); outputStream.flush(); } catch (IOException e) { e.printStackTrace(); } } } }).start(); handler.post(new Runnable() { @Override public void run() { tv_play.setText("运动状态:正在右转...."); } }); } if (angle > 45 && angle < 135) {//前进 new Thread(new Runnable() { @Override public void run() { send_buff = forward; OutputStream outputStream = null; try { outputStream = socket.getOutputStream(); } catch (IOException e) { e.printStackTrace(); } if (outputStream != null) { try { outputStream.write(send_buff.getBytes()); outputStream.flush(); } catch (IOException e) { e.printStackTrace(); } } } }).start(); handler.post(new Runnable() { @Override public void run() { tv_play.setText("运动状态:正在前进...."); } }); } if (angle > 135 && angle < 225) {//左转 new Thread(new Runnable() { @Override public void run() { send_buff = left; OutputStream outputStream = null; try { outputStream = socket.getOutputStream(); } catch (IOException e) { e.printStackTrace(); } if (outputStream != null) { try { outputStream.write(send_buff.getBytes()); outputStream.flush(); } catch (IOException e) { e.printStackTrace(); } } } }).start(); handler.post(new Runnable() { @Override public void run() { tv_play.setText("运动状态:正在左转...."); } }); } if (angle > 225 && angle < 315) {//后退 new Thread(new Runnable() { @Override public void run() { send_buff = back; OutputStream outputStream = null; try { outputStream = socket.getOutputStream(); } catch (IOException e) { e.printStackTrace(); } if (outputStream != null) { try { outputStream.write(send_buff.getBytes()); outputStream.flush(); } catch (IOException e) { e.printStackTrace(); } } } }).start(); handler.post(new Runnable() { @Override public void run() { tv_play.setText("运动状态:正在后退...."); } }); } } } }); btn_stop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { send_buff = stop; OutputStream outputStream = null; try { outputStream = socket.getOutputStream(); } catch (IOException e) { e.printStackTrace(); } if (outputStream != null) { try { outputStream.write(send_buff.getBytes()); outputStream.flush(); } catch (IOException e) { e.printStackTrace(); } } } }).start(); handler.post(new Runnable() { @Override public void run() { tv_play.setText("运动状态:停止...."); } }); } }); } private void init() { tv = findViewById(R.id.tv); tv_connect = findViewById(R.id.tv_connect); tv_play = findViewById(R.id.tv_play); btn_stop=findViewById(R.id.btn_stop); handler = new Handler(); rudder = findViewById(R.id.rudder); } private void recv() { //单开一个线程循环接收来自服务器端的消息 InputStream inputStream = null; try { inputStream = socket.getInputStream(); } catch (IOException e) { e.printStackTrace(); } if (inputStream != null) { try { byte[] buffer = new byte[1024]; int count = inputStream.read(buffer);//count是传输的字节数 recv_buff = new String(buffer);//socket通信传输的是byte类型,需要转为String类型 } catch (IOException e) { e.printStackTrace(); } } //显示连接状态 if (recv_buff != null) { handler.post(runnableUi); } } //利用handler刷新UI Runnable runnableUi = new Runnable() { @Override public void run() { tv_connect.append(recv_buff); } }; }



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有