• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

树莓派小车的4G遥控和视频回传内网穿透

武飞扬头像
黑名单 2841703718
帮助1

目录

1.项目简介

2.工具

3.步骤

1.手机端

(1)设置ip和端口

(2)遥控界面

2.树莓派

(1)给树莓派配置公网环境

(2)树莓派进行内网穿透

(3)树莓派UDP监听

(4)树莓派视频回传

3.电脑端

(1)电脑进行内网穿透

(2)视频接收代码


1.项目简介

基于公网环境进行UDP通信,使用手机APP发出指令,控制树莓派小车移动,并将树莓派小车搭载的摄像头采集到的画面回传到电脑上显示。

2.工具

Android studio(开发手机APP)

PyCharm Community Edition (电脑端python程序接收树莓派回传的视频)

网卡 SIM7600CE—CNSE(树莓派联网)

软件:natapp,花生壳(内网穿透)

树莓派小车,摄像头

3.步骤

1.手机端

(1)设置ip和端口

Android studio开发通信APP,这是ip和端口的设置界面及代码

学新通

  1.  
    public class MainActivity extends AppCompatActivity {
  2.  
    private Button btn1;
  3.  
    private EditText ip;
  4.  
    private EditText port;
  5.  
    private SharedPreferences.Editor spE;
  6.  
    private SharedPreferences sp;;
  7.  
     
  8.  
    @Override
  9.  
    protected void onCreate(Bundle savedInstanceState) {
  10.  
    super.onCreate(savedInstanceState);
  11.  
    setContentView(R.layout.activity_main);
  12.  
    btn1 = findViewById(R.id.bt_1);
  13.  
    ip = findViewById(R.id.et_1);
  14.  
    port = findViewById(R.id.et_2);
  15.  
     
  16.  
    sp = getSharedPreferences("ip", MODE_PRIVATE);
  17.  
    sp = getSharedPreferences("port", MODE_PRIVATE);
  18.  
    spE = sp.edit();
  19.  
     
  20.  
    ip.setText(sp.getString("ip", ""));
  21.  
    port.setText(sp.getString("port",""));
  22.  
     
  23.  
    btn1.setOnClickListener(new View.OnClickListener() {
  24.  
    @Override
  25.  
     
  26.  
    public void onClick(View v) {
  27.  
    if (ip.getText().toString().isEmpty() || port.getText().toString().isEmpty()) {
  28.  
    Toast.makeText(MainActivity.this, "请输入ip地址", Toast.LENGTH_SHORT).show();
  29.  
    //弹窗
  30.  
    return;
  31.  
    }
  32.  
     
  33.  
    spE.putString("ip", ip.getText().toString());
  34.  
    spE.putString("port", port.getText().toString());
  35.  
    spE.apply();
  36.  
     
  37.  
    Intent intent = new Intent(MainActivity.this, MainActivity_udp.class);
  38.  
    startActivity(intent);
  39.  
    }
  40.  
    });
  41.  
    }
  42.  
    }
学新通

(2)遥控界面

这是遥控界面的布局图片和代码,点击对应的图标就会有相应的指令发出

学新通

  1.  
    public class MainActivity3 extends AppCompatActivity
  2.  
    implements View.OnTouchListener
  3.  
    {
  4.  
    private SharedPreferences sp;
  5.  
    @SuppressLint("ClickableViewAccessibility")
  6.  
    @Override
  7.  
    protected void onCreate(Bundle savedInstanceState) {
  8.  
    super.onCreate(savedInstanceState);
  9.  
    setContentView(R.layout.activity_main3);
  10.  
     
  11.  
    TextView tx_w =findViewById(R.id.tx_w);
  12.  
    TextView tx_s =findViewById(R.id.tx_s);
  13.  
    TextView tx_a =findViewById(R.id.tx_a);
  14.  
    TextView tx_d =findViewById(R.id.tx_d);
  15.  
     
  16.  
    tx_w.setOnTouchListener(MainActivity3.this);
  17.  
    tx_s.setOnTouchListener(MainActivity3.this);
  18.  
    tx_a.setOnTouchListener(MainActivity3.this);
  19.  
    tx_d.setOnTouchListener(MainActivity3.this);
  20.  
     
  21.  
    sp = getSharedPreferences("ip",MODE_PRIVATE);
  22.  
    sp = getSharedPreferences("port",MODE_PRIVATE);
  23.  
     
  24.  
    }
  25.  
    @SuppressLint({"ClickableViewAccessibility", "NonConstantResourceId"})
  26.  
    @Override
  27.  
    public boolean onTouch(View v, MotionEvent event) {
  28.  
    switch (v.getId()) {
  29.  
    case R.id.tx_w:
  30.  
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
  31.  
    udp_Thread ut = new udp_Thread("w", sp.getString("ip", ""),sp.getString("port",""));
  32.  
    ut.start();
  33.  
     
  34.  
    }
  35.  
     
  36.  
    // 抬起操作
  37.  
    else if (event.getAction() == MotionEvent.ACTION_UP) {
  38.  
    udp_Thread ut = new udp_Thread("p" , sp.getString("ip", ""),sp.getString("port",""));
  39.  
    ut.start();
  40.  
    }
  41.  
    return true;
  42.  
     
  43.  
    case R.id.tx_s:
  44.  
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
  45.  
    udp_Thread ut = new udp_Thread("s" , sp.getString("ip", ""),sp.getString("port",""));
  46.  
    ut.start();
  47.  
    }
  48.  
    // 抬起操作
  49.  
    else if (event.getAction() == MotionEvent.ACTION_UP) {
  50.  
    udp_Thread ut = new udp_Thread("p", sp.getString("ip", ""),sp.getString("port",""));
  51.  
    ut.start();
  52.  
    }
  53.  
    return true;
  54.  
    case R.id.tx_a:
  55.  
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
  56.  
    udp_Thread ut = new udp_Thread("a" , sp.getString("ip", ""),sp.getString("port",""));
  57.  
    ut.start();
  58.  
    }
  59.  
    // 抬起操作
  60.  
    else if (event.getAction() == MotionEvent.ACTION_UP) {
  61.  
    udp_Thread ut = new udp_Thread("c" , sp.getString("ip", ""), sp.getString("port",""));
  62.  
    ut.start();
  63.  
    }
  64.  
    return true;
  65.  
    case R.id.tx_d:
  66.  
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
  67.  
     
  68.  
    udp_Thread ut = new udp_Thread("d" , sp.getString("ip", ""), sp.getString("port",""));
  69.  
    ut.start();
  70.  
    }
  71.  
    // 抬起操作
  72.  
    else if (event.getAction() == MotionEvent.ACTION_UP) {
  73.  
     
  74.  
    udp_Thread ut = new udp_Thread("c" , sp.getString("ip", ""), sp.getString("port",""));
  75.  
    ut.start();
  76.  
    }
  77.  
    return true;
  78.  
    }
  79.  
    return true;
  80.  
    }
  81.  
    }
学新通

 工具类udp_Thread(udp通信代码)

  1.  
    public class udp_Thread extends Thread {
  2.  
    private String str;
  3.  
    private String ip;
  4.  
    private String port;
  5.  
     
  6.  
    public udp_Thread(String str, String ip, String port) {
  7.  
    this.str = str;
  8.  
    this.ip = ip;
  9.  
    this.port = port;
  10.  
    }
  11.  
     
  12.  
    @Override
  13.  
    public void run() {
  14.  
    try {
  15.  
    Thread.sleep(100);
  16.  
    } catch (InterruptedException e) {
  17.  
    e.printStackTrace();
  18.  
    }
  19.  
    DatagramSocket socket = null;
  20.  
    try {
  21.  
    if (socket == null) {
  22.  
    socket = new DatagramSocket(null);
  23.  
    socket.setReuseAddress(true);
  24.  
    socket.bind(new InetSocketAddress(Integer.parseInt(port)));
  25.  
    }
  26.  
    } catch (SocketException e) {
  27.  
    e.printStackTrace();
  28.  
    }//实例化socket套接字
  29.  
     
  30.  
    InetAddress serverAddress = null;
  31.  
    try {
  32.  
    serverAddress = InetAddress.getByName(ip);
  33.  
    } catch (UnknownHostException e) {
  34.  
    e.printStackTrace();
  35.  
    }//设置ip
  36.  
     
  37.  
    byte[] data = str.getBytes();
  38.  
    System.out.println(str);
  39.  
    System.out.println(ip);
  40.  
    System.out.println(port);
  41.  
    DatagramPacket packages = new DatagramPacket(data, data.length, serverAddress, Integer.parseInt(port));
  42.  
    //封装报文
  43.  
     
  44.  
     
  45.  
    try {
  46.  
    assert socket != null;
  47.  
    socket.send(packages);
  48.  
    } catch (IOException e) {
  49.  
    e.printStackTrace();
  50.  
    }//发送
  51.  
     
  52.  
    socket.close();
  53.  
    }
  54.  
    }
学新通

至此,手机APP已经编写好了,设置正确的IP和端口就可以将指令发到树莓派上了

(W:电机正转 S:电机反转 A:舵机左转 D:舵机右转 P:电机停止 C:舵机回正)

2.树莓派

(1)给树莓派配置公网环境

我用的这种 网卡 (主要是支持Linux系统,比较方便)淘宝上买的,有点小贵。

也可以使用电脑上插得那种网卡,然后在树莓派上配置它的驱动

这是拨号教程

(2)树莓派进行内网穿透

1.不进行内网穿透的话,树莓派的ip(不管是局域网还是移动网络的ip都不是公网ip)只是在当前节点的局域网内的ip,不能进行公网通信。

2.穿透之后相当于给树莓派ip在公网环境中找了个爹(公网ip),手机APP的指令先发到他爹哪里(公网ip),他爹在转发给树莓派ip(节点内局域网ip)

4.配置好内网穿透之后,在APP的ip和端口设置界面输入ip和端口号,就可以发送指令到树莓派了。(server.natappfree.cc是域名,转换成ip就行了)

(3)树莓派UDP监听

树莓派里面运行python程序

  1.  
    sockfd=socket(AF_INET,SOCK_DGRAM)
  2.  
    server_addr=('0.0.0.0',8003)
  3.  
    sockfd.bind(server_addr)
  4.  
     
  5.  
    def S_UDP():
  6.  
    data,addr=sockfd.recvfrom(1024)
  7.  
    if not data:
  8.  
    print(waiting)
  9.  
    id= data.decode()
  10.  
    print('收到的信息:\nid:%s' % (id))
  11.  
     
  12.  
    if __name__ == "__main__":
  13.  
    while True:
  14.  
    S_UDP()

这样树莓派就可以接收到来自手机APP的指令了。

然后根据接收到的指令控制小车的移动

  1.  
    if order == 'a':
  2.  
    Motor.ChangeDutyCycle(MotorCenter Motor_sensitivity)
  3.  
     
  4.  
    elif order == 'd':
  5.  
    Motor.ChangeDutyCycle(MotorCenter - Motor_sensitivity)
  6.  
     
  7.  
    if order == 'w':
  8.  
    Servo.ChangeDutyCycle(ServoCenter Servo_sensitivity)
  9.  
     
  10.  
    elif order == 's':
  11.  
    Servo.ChangeDutyCycle(ServoCenter - Servo_sensitivity)
  12.  
     
  13.  
     
  14.  
    if order == 'c':
  15.  
    Motor.ChangeDutyCycle(MotorCenter)
  16.  
     
  17.  
    if order == 'p':
  18.  
    Servo.ChangeDutyCycle(ServoCenter)
学新通

控制小车移动的代码取决于你使用的树莓派小车的底层,我这个只做参考

(4)树莓派视频回传

代码运行需要opencv,安装教程网上比较多,不在赘述,记得换源就行了

  1.  
     
  2.  
    import cv2
  3.  
    import socket
  4.  
     
  5.  
    IP=103.46.128.49
  6.  
    PORT=13415
  7.  
    server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
  8.  
     
  9.  
    WIDTH=320
  10.  
    HEIGHT=240
  11.  
     
  12.  
    print('now starting to send frames...')
  13.  
    capture=cv2.VideoCapture(0)
  14.  
    capture.set(cv2.CAP_PROP_FRAME_WIDTH,WIDTH)
  15.  
    capture.set(cv2.CAP_PROP_FRAME_HEIGHT,HEIGHT)
  16.  
    try:
  17.  
    while True:
  18.  
    success,frame=capture.read()
  19.  
    if success and frame is not None:
  20.  
    result,imgencode=cv2.imencode('.jpg',frame,[cv2.IMWRITE_JPEG_QUALITY,90])
  21.  
    server.sendto(imgencode,(IP, PORT))
  22.  
     
  23.  
    except Exception as e:
  24.  
    print(e)
  25.  
    capture.release()
  26.  
    server.close()
学新通

视频回传是回传到电脑端,所以这里的ip要设置电脑的公网ip(电脑内网穿透获取到的公网ip)

3.电脑端

(1)电脑进行内网穿透

电脑端使用花生壳配置内网穿透,下载花生壳,操作很简单,配置成功后如图所示:

学新通

如图所示,右上角访问地址4y4399t29.wicp.vip:13415就是电脑内网穿透之后获取的公网ip和端口4y4399t29.wicp.vip是域名(对应ip:103.46.128.49),13415是端口。内网主机就是自己电脑的ip和端口,端口在运行的python程序中设置,ip查看方法如下:

键盘按Windows R输入“cmd”并确定后,命令窗口中输入“ipconfig”,回车,找到ipv4对应的ip地址,就是你电脑的ip地址。

配置成功后向103.46.128.49:13415发送的数据就会转发到你的电脑上(100.23.89.197:8888)

(2)视频接收代码

  1.  
    import cv2
  2.  
    import numpy
  3.  
    import socket
  4.  
     
  5.  
    HOST = '0.0.0.0'
  6.  
    PORT = 8888
  7.  
    buffSize = 65535
  8.  
     
  9.  
    server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 创建socket对象
  10.  
    server.bind((HOST, PORT))
  11.  
    print('now waiting for frames...')
  12.  
    while True:
  13.  
    data, address = server.recvfrom(buffSize) # 先接收的是字节长度
  14.  
    data = numpy.array(bytearray(data)) # 格式转换
  15.  
    imgdecode = cv2.imdecode(data, 1) # 解码
  16.  
    print('have received one frame')
  17.  
    cv2.imshow('frames', imgdecode) # 窗口显示
  18.  
    if cv2.waitKey(1) == 27:
  19.  
    break
  20.  
    server.close()
  21.  
    cv2.destroyAllWindows()
学新通

电脑收到树莓派发送的视频后就会显示在屏幕上。

第一次写文章,分享自己玩的树莓派4G小车,看的不明白的地方留言提问。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbeief
系列文章
更多 icon
同类精品
更多 icon
继续加载