草庐IT

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

黑名单 2841703718 2023-10-28 原文

目录

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和端口的设置界面及代码

public class MainActivity extends AppCompatActivity {
    private Button btn1;
    private EditText ip;
    private EditText port;
    private SharedPreferences.Editor spE;
    private SharedPreferences sp;;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn1 = findViewById(R.id.bt_1);
        ip = findViewById(R.id.et_1);
        port = findViewById(R.id.et_2);

        sp = getSharedPreferences("ip", MODE_PRIVATE);
        sp = getSharedPreferences("port", MODE_PRIVATE);
        spE = sp.edit();

        ip.setText(sp.getString("ip", ""));
        port.setText(sp.getString("port",""));

        btn1.setOnClickListener(new View.OnClickListener() {
            @Override

            public void onClick(View v) {
                if (ip.getText().toString().isEmpty() || port.getText().toString().isEmpty()) {
                    Toast.makeText(MainActivity.this, "请输入ip地址", Toast.LENGTH_SHORT).show();
                    //弹窗
                    return;
                }

                spE.putString("ip", ip.getText().toString());
                spE.putString("port", port.getText().toString());
                spE.apply();

                Intent intent = new Intent(MainActivity.this, MainActivity_udp.class);
                startActivity(intent);
            }
        });
    }
}

(2)遥控界面

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

public class MainActivity3 extends AppCompatActivity
        implements View.OnTouchListener
{
    private SharedPreferences sp;
    @SuppressLint("ClickableViewAccessibility")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);

        TextView tx_w =findViewById(R.id.tx_w);
        TextView tx_s =findViewById(R.id.tx_s);
        TextView tx_a =findViewById(R.id.tx_a);
        TextView tx_d =findViewById(R.id.tx_d);

        tx_w.setOnTouchListener(MainActivity3.this);
        tx_s.setOnTouchListener(MainActivity3.this);
        tx_a.setOnTouchListener(MainActivity3.this);
        tx_d.setOnTouchListener(MainActivity3.this);

        sp = getSharedPreferences("ip",MODE_PRIVATE);
        sp = getSharedPreferences("port",MODE_PRIVATE);
   
    }
    @SuppressLint({"ClickableViewAccessibility", "NonConstantResourceId"})
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (v.getId()) {
            case R.id.tx_w:
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                        udp_Thread ut = new udp_Thread("w", sp.getString("ip", ""),sp.getString("port",""));
                        ut.start();

                }

                // 抬起操作
                else if (event.getAction() == MotionEvent.ACTION_UP) {
                    udp_Thread ut = new udp_Thread("p" , sp.getString("ip", ""),sp.getString("port",""));
                    ut.start();
                }
                return true;

            case R.id.tx_s:
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    udp_Thread ut = new udp_Thread("s" , sp.getString("ip", ""),sp.getString("port",""));
                    ut.start();
                }
                // 抬起操作
                else if (event.getAction() == MotionEvent.ACTION_UP) {
                    udp_Thread ut = new udp_Thread("p", sp.getString("ip", ""),sp.getString("port",""));
                    ut.start();
                }
                return true;
            case R.id.tx_a:
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    udp_Thread ut = new udp_Thread("a" , sp.getString("ip", ""),sp.getString("port",""));
                    ut.start();
                }
                // 抬起操作
                else if (event.getAction() == MotionEvent.ACTION_UP) {
                    udp_Thread ut = new udp_Thread("c" , sp.getString("ip", ""), sp.getString("port",""));
                    ut.start();
                }
                return true;
            case R.id.tx_d:
                if (event.getAction() == MotionEvent.ACTION_DOWN) {

                    udp_Thread ut = new udp_Thread("d" , sp.getString("ip", ""), sp.getString("port",""));
                    ut.start();
                }
                // 抬起操作
                else if (event.getAction() == MotionEvent.ACTION_UP) {

                    udp_Thread ut = new udp_Thread("c" , sp.getString("ip", ""), sp.getString("port",""));
                    ut.start();
                }
                return true;
        }
        return true;
    }
}

 工具类udp_Thread(udp通信代码)

public class udp_Thread extends Thread {
    private String str;
    private String ip;
    private String port;

    public udp_Thread(String str, String ip, String port) {
        this.str = str;
        this.ip = ip;
        this.port = port;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        DatagramSocket socket = null;
        try {
            if (socket == null) {
                socket = new DatagramSocket(null);
                socket.setReuseAddress(true);
                socket.bind(new InetSocketAddress(Integer.parseInt(port)));
            }
        } catch (SocketException e) {
            e.printStackTrace();
        }//实例化socket套接字

        InetAddress serverAddress = null;
        try {
            serverAddress = InetAddress.getByName(ip);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }//设置ip

        byte[] data = str.getBytes();
        System.out.println(str);
        System.out.println(ip);
        System.out.println(port);
        DatagramPacket packages = new DatagramPacket(data, data.length, serverAddress, Integer.parseInt(port));
        //封装报文


            try {
                assert socket != null;
                socket.send(packages);
            } catch (IOException e) {
                e.printStackTrace();
            }//发送

            socket.close();
        }
    }

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

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

2.树莓派

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

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

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

这是拨号教程

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

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

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

3.具体步骤可以参考这篇文章NATAPP使用笔记—内网穿透,透内通外_信看的博客-CSDN博客。(使用普通网卡的也可以参考这个,不局限于网卡设备)

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

(3)树莓派UDP监听

树莓派里面运行python程序

sockfd=socket(AF_INET,SOCK_DGRAM)
server_addr=('0.0.0.0',8003)
sockfd.bind(server_addr)

def S_UDP():
    data,addr=sockfd.recvfrom(1024)
    if not data:
        print(waiting)
    id= data.decode()
    print('收到的信息:\nid:%s' % (id))
           
if __name__ == "__main__":
    while True:
        S_UDP()

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

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

 if order == 'a':
        Motor.ChangeDutyCycle(MotorCenter + Motor_sensitivity)

    elif order == 'd':
        Motor.ChangeDutyCycle(MotorCenter - Motor_sensitivity)

    if order == 'w':
        Servo.ChangeDutyCycle(ServoCenter + Servo_sensitivity)

    elif order == 's':
        Servo.ChangeDutyCycle(ServoCenter - Servo_sensitivity)
        

    if order == 'c':
        Motor.ChangeDutyCycle(MotorCenter)

    if order == 'p':
        Servo.ChangeDutyCycle(ServoCenter)

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

(4)树莓派视频回传

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


import cv2
import socket

IP=103.46.128.49
PORT=13415
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

WIDTH=320
HEIGHT=240

print('now starting to send frames...')
capture=cv2.VideoCapture(0)
capture.set(cv2.CAP_PROP_FRAME_WIDTH,WIDTH)
capture.set(cv2.CAP_PROP_FRAME_HEIGHT,HEIGHT)
try:
    while True:
        success,frame=capture.read()
        if success and frame is not None:
            result,imgencode=cv2.imencode('.jpg',frame,[cv2.IMWRITE_JPEG_QUALITY,90])
            server.sendto(imgencode,(IP, PORT))
            
except Exception as e:
    print(e)
    capture.release()
    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)视频接收代码

import cv2
import numpy
import socket

HOST = '0.0.0.0'
PORT = 8888
buffSize = 65535

server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 创建socket对象
server.bind((HOST, PORT))
print('now waiting for frames...')
while True:
    data, address = server.recvfrom(buffSize)  # 先接收的是字节长度
    data = numpy.array(bytearray(data))  # 格式转换
    imgdecode = cv2.imdecode(data, 1)  # 解码
    print('have received one frame')
    cv2.imshow('frames', imgdecode)  # 窗口显示
    if cv2.waitKey(1) == 27:
        break
server.close()
cv2.destroyAllWindows()

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

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

有关树莓派小车的4G遥控与视频回传(内网穿透)的更多相关文章

  1. 动漫制作技巧如何制作动漫视频 - 2

    动漫制作技巧是很多新人想了解的问题,今天小编就来解答与大家分享一下动漫制作流程,为了帮助有兴趣的同学理解,大多数人会选择动漫培训机构,那么今天小编就带大家来看看动漫制作要掌握哪些技巧?一、动漫作品首先完成草图设计和原型制作。设计草图要有目的、有对象、有步骤、要形象、要简单、符合实际。设计图要一致性,以保证制作的顺利进行。二、原型制作是根据设计图纸和制作材料,可以是手绘也可以是3d软件创建。在此步骤中,要注意的问题是色彩和平面布局。三、动漫制作制作完成后,加工成型。完成不同的表现形式后,就要对设计稿进行加工处理,使加工的难易度降低,并得到一些基本准确的概念,以便于后续的大样、准确的尺寸制定。四、

  2. python ffmpeg 使用 pyav 转换 一组图像 到 视频 - 2

    2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p

  3. TimeSformer:抛弃CNN的Transformer视频理解框架 - 2

    Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图

  4. ruby - 如何更改此正则表达式以从未指定 v 参数的 Youtube URL 获取 Youtube 视频 ID? - 2

    目前我正在使用这个正则表达式从YoutubeURL中提取视频ID:url.match(/v=([^&]*)/)[1]我怎样才能改变它,以便它也可以从这个没有v参数的YoutubeURL获取视频ID:http://www.youtube.com/user/SHAYTARDS#p/u/9/Xc81AajGUMU感谢阅读。编辑:我正在使用ruby​​1.8.7 最佳答案 对于Ruby1.8.7,这就可以了。url_1='http://www.youtube.com/watch?v=8WVTOUh53QY&feature=feedf'url

  5. 续集来了丨UI自动化测试(二):带视频,实在RPA高效进行web项目UI自动化测试 - 2

    一、什么是web项目ui自动化测试?通过测试工具模拟人为操控浏览器,使软件按照测试人员的预定计划自动执行测试的一种方式,可以完成许多手工测试无法完成或者不易实现的繁琐工作。正确使用自动化测试,可以更全面的对软件进行测试,从而提高软件质量进而缩短迭代周期。二、构建测试用例的“九部曲”(一)创建流程包划分功能模块日常测试活动中,都会根据功能模块进行拆分,所以在设计器中我们可以通过创建流程包的方式来拆分需要测试的功能模块,如下图中操作创建一个电脑流程包并且取名为对应的功能模块名称,如果有多个功能模块就创建多个对应的流程包,实在RPA设计器有易用的图形可视化界面,方便管理较多的功能模块。(二)在流程包

  6. Java调用ffmpeg处理视频,并记录下遇到的坑 - 2

    目录需求基于JavaCV跨平台执行ffmpeg命令[^1]坑一内存不足坑二多个ffmpeg进程并行导致IO负载大,进而导致ioerror?坑三使用Java操作ffmpeg时,有时会卡死坑四Process的waitFor死锁问题及解决办法需求给透明背景的视频自动叠加一张背景图片基于JavaCV跨平台执行ffmpeg命令1我测试发现的本需求的最小依赖:dependency>groupId>org.bytedecogroupId>artifactId>ffmpeg-platform-gplartifactId>version>5.0-1.5.7version>dependency>核心代码:Stri

  7. 基于python的短视频智能推荐/django的影视网站/视频推荐系统 - 2

    摘要本论文主要论述了如何使用Python技术开发一个短视频智能推荐,本系统将严格按照软件开发流程进行各个阶段的工作,采用B/S架构,面向对象编程思想进行项目开发。在引言中,作者将论述短视频智能推荐的当前背景以及系统开发的目的,后续章节将严格按照软件开发流程,对系统进行各个阶段分析设计。 短视频智能推荐的主要使用者分为管理员和用户,实现功能包括管理员:首页、个人中心、用户管理、热门视频管理、用户上传管理、系统管理,用户:首页、个人中心、用户上传管理、我的收藏管理,前台首页;首页、热门视频、用户上传、公告信息、个人中心、后台管理等功能。由于本网站的功能模块设计比较全面,所以使得整个短视频智能推荐信

  8. 基于ffmpeg的视频处理与MPEG的压缩试验(下载安装使用全流程) - 2

    基于ffmpeg的视频处理与MPEG的压缩试验ffmpeg介绍与基础知识对提取到的图像进行处理RGB并转化为YUV对YUV进行DCT变换对每个8*8的图像块进行进行量化操作ffmpeg介绍与基础知识ffmpeg是视频和图像处理的工具包,它的下载网址是https://ffmpeg.org/download.html。页面都是英文且下载正确的包的路径笔者找的时候还费点劲,这里记录一下也方便读者。选中这个Windows下的下午files,选择第一个这里有essential和full版本的,大家根据需要自行选择版本包下载下载好之后,在官网上下载ffmpeg的full包,一共300+MB解压,然后安装b

  9. 视频融合技术解决方案,三维全景拼接赋能平台 - 2

    近年来,随着信息化时代的到来,三维全景拼接以视频监控领域为代表的智能硬件公司迅速崛起,随后全国各地在视频监控领域进行了大量的建设。但随着摄像头数量的增加,视频监控画面离散、庞杂、关联性差等诸多问题日渐凸显。如何优化现有视频技术,助力管理者或使用者有效、直观、准确地掌控现场实时动态,成为我国信息化前行路上面临的新课题。视频融合技术平台解决方案北京智汇云舟科技有限公司成立于2012年,专注于创新性的“视频孪生(实时实景数字孪生)”技术研发与应用。公司依托自研三维地理信息引擎(3DGIS),融合建筑信息模型(BIM)、视频监控(Video)、人工智能(AI)及物联网(IOT)等多种技术,并在此基础上

  10. 火山引擎 RTC 视频性能降级策略解析 - 2

    动手点关注干货不迷路1. 背景随着RTC使用场景的不断复杂化,新特性不断增多,同时用户对清晰度提升的诉求也越来越强烈,这些都对客户端机器性能提出了越来越高的要求(越来越高的分辨率,越来越复杂的编码器等)。但机器性能差异千差万别,同时用户的操作也不可预知,高级特性的使用和机器性能的矛盾客观存在。当用户机器负载过高时,我们需要适当降级视频特性来减轻系统复杂性,确保重要功能正常使用,提升用户体验。视频性能降级能做什么?一是解决因设备性能不足、突发的性能消耗冲击(如杀毒软件)带来的用户音视频体验问题(如视频卡顿、延时高、设备卡死)等问题;二是提升一些高级功能的渗透率,例如默认情况下开启视频超分,设备性

随机推荐