百度智能车竞赛丝绸之路2——手柄控制

news2025/1/24 5:05:28

百度智能车竞赛丝绸之路1——智能车设计与编程实现控制
百度智能车竞赛丝绸之路2——手柄控制

一、机器人设计

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、实现原理

本教程使用Python的Serial库和Struct二进制数据解析库去实现Xbox手柄+百度大脑学习开发板(上位机)和机器人控制器(下位机)之间的通讯。
Xbox通过蓝牙模块与上位机进行通讯,上位机用Python中的Struct库去将蓝牙模块接收到的数据解析成四个元素的元组(时间、数据、类型、编号)。时间数据表示手柄与上位机每次发送字节数据进行通讯的时间戳,数据则表示手柄传给上位机的数据(value),其中button数据为{1,0}(离散值)、axis数据为{-32767,32768}(同样是离散值:2的16次方,也是代表两个字节数),类型数据包括{1,0}(0代表包含的数据为button值可以用int类型变量接收,1代表包含的数据为axis值可以用float类型变量接收),编号数据是对于手柄不同按键的排序。通过解析出来的手柄数据进而使上位机对机器人下位机进行通讯,实现了上位机对于机器人各个关节及其功能的控制。与下位机的通讯是基于485串口通讯协议进行控制的,机器人控制器只有一个串口,而与串口之间通讯同一时间只能通讯一次,也是后续项目开发过程中的难题所在。

手柄数据用button(int)列表和axis(float)列表接收,用joy_listenner-_thread线程实时对手柄数据进行解析并赋值给类成员变量self.button与self.axis。之后在手柄控制程序中实例化对象并开启类内成员线程。将与下位机通讯的数据与手柄数据相联系即可。手柄功能描述如下所示:
在这里插入图片描述
其中,更换模式可以更换手柄控制模式和自主巡检(做任务)模式,并且当手柄更换模式的时候蜂鸣笛响一声,RGB灯光切换灯光模式(闪烁和绿色)。而当程序退出时RGB等则呈现红色。当手柄更换只巡航模式是,灯光与蜂鸣笛状态都会发生改变,这是为了方便控制者的控制。
手柄控制模式中,防止各个机器人关节发生卡死现象(电机卡死不转动),需要对机器人各个关节电机做出限位,如下表所示:
在这里插入图片描述

三、编程实现

手柄解析joystick_struct.py

class JoyStick:
    def __init__(self):
        print('avaliable devices')
        for fn in os.listdir('/dev/input'):
            if fn.startswith('js'):
                print('/dev/input/%s' % fn)
        self.fn = '/dev/input/js0'
        self.datas = [0] * 4
        self.ev_buf = None
        self.js_dev = None
        self.done = True
        self.flag = True
        self.button = [0] * 15
        self.axis = [0.0] * 15
        self.listener_thread = Thread(target=self.read, daemon=True, name='read')
        self.logs = False

    def open(self):
        self.js_dev = open(self.fn, 'rb')

    # time, value, type_, number
    def read(self):
        while self.done and self.flag:
            self.ev_buf = self.js_dev.read(8)
            self.datas = struct.unpack('IhBB', self.ev_buf)
            self.put_data()

    def put_data(self):
        if self.datas[2] & 0x01:
            self.button[self.datas[3]] = self.datas[1]
        if self.datas[2] & 0x02:
            self.axis[self.datas[3]] = self.datas[1] / 32767
        if self.logs:
            print(self.button)
            print(self.axis)

    def __del__(self):
        self.done = False

多线程检测thread_detection.py

from detector.detectors import Cruiser, TaskDetector, SignDetector
import threading
import cv2


class Mylane(threading.Thread):
    def __init__(self, front_camera, cruiser: Cruiser, signdetector: SignDetector):
        super().__init__()  # 要先调用父类的init方法否则会报错
        self.Done = True

        # self.front_camera = cv2.VideoCapture(1)  # 主摄像头为1
        self.front_camera = front_camera
        self.front_camera.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
        self.front_camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
        self.signdetector = signdetector
        self.cruiser = cruiser

        self.return_ = 0.0
        self.landmark = 0
        self.land_mark_thread = threading.Thread(target=self.land_mark, daemon=True, name='land_mark')
        ret, self.front_image = self.front_camera.read()
        self.land_mark_thread.start()

    def land_mark(self):
        while self.Done:
            get_landmark = self.signdetector.detect(self.front_image)
            # self.landmark = 0
            print("get_landmark",get_landmark)
            # print("总地标识别:", get_landmark)ssss
            if len(get_landmark) == 0:  # 未识别到结果 程序一直执行
                self.landmark = 0
            if len(get_landmark) != 0 and (
                    (str(get_landmark[0])[13:21] == "castle s" and str(get_landmark[0])[26:30] >= "0.80") or (str(get_landmark[0])[13:21] == "friendsh" and str(get_landmark[0])[30:34] >= "0.80")):
                # print("get_landmark", str(get_landmark[0])[13:21])
                self.landmark = 1

    def run(self):
        while self.Done:
            ret, self.front_image = self.front_camera.read()
            # print("front_image.shape:",front_image.shape)
            self.return_ = self.cruiser.infer_cnn(self.front_image)

    def __del__(self):
        self.land_mark_thread.join()


class MyDetect(threading.Thread):
    def __init__(self, side_camera, detector: TaskDetector):
        super().__init__()  # 要先调用父类的init方法否则会报错
        self.detector = detector
        # self.side_camera = cv2.VideoCapture(0)  # 侧面摄像头为0
        self.side_camera = side_camera
        self.side_camera.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
        self.side_camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
        self.Done = True
        self.have_done = [False] * 5
        self.return_ = 0

    def run(self):
        while self.Done:
            ret, side_image = self.side_camera.read()
            # print("side_image.shape:", side_image.shape)
            res_img = self.detector.detect(side_image)
            # self.res_img=["dunhuang"]
            # print(res_img)
            result = res_img
            print("***********************")
            print(result)
            print("***************************")
            # print("result:",str(result[0])[13:21])
            if len(result) == 0:  # 未识别到结果 程序一直执行
                self.return_ = 0
            if len(result) != 0 and str(result[0])[13:21] == "alamutu ":  # 识别到阿拉图 跳出循环 返回1
                self.return_ = 1
                # break
            if len(result) != 0 and str(result[0])[13:21] == "dunhuang":  # 识别到敦煌 跳出循环 返回2
                self.return_ = 2
                # break
            if len(result) != 0 and str(result[0])[13:21] == "jstdb sc":  # 识别到君士坦丁堡 跳出循环 返回3
                self.return_ = 3
                # break
            if len(result) != 0 and str(result[0])[13:21] == "friendsh":  # 识别到圣歌 跳出循环 返回4
                # print(str(result[0])[13:21])
                self.return_ = 4

手柄控制chassis_control.py

class RobotController:

    def __init__(self, joy: pyjoy.JoyStick, _serial: serial.Serial):
        self._serial = _serial
        self.controller = MechainArmController(self._serial)
        self.chassis_ = ChassisController(self._serial)
        self.sensor_ = SensorController(self._serial)
        self.compensator_ = CompensatorController(self._serial)
        self.raise_flag_ = RaiseFlag(self._serial)

        self.joy = joy

        self.Break = False

    def begin_control(self):
        while True:
            if self.joy.button[8] or self.joy.button[9]:
                break
            if self.joy.button[4] == 1:
                self.controller.servo_control(90, 30)
            if self.joy.button[5] == 1:
                self.controller.servo_control(135, 30)

            if self.joy.button[0] == 1:
                self.raise_flag_.servo_control(self.raise_flag_.angle[0], 50)
            elif self.joy.button[1] == 1:
                self.raise_flag_.servo_control(self.raise_flag_.angle[1], 50)
            elif self.joy.button[2] == 1:
                self.raise_flag_.servo_control(self.raise_flag_.angle[2], 50)
            elif self.joy.button[3] == 1:
                self.raise_flag_.servo_control(self.raise_flag_.angle[3], 50)
            else:
                pass

            if self.joy.axis[5] == -1:
                self.controller.motor_rotate(40)
            elif self.joy.axis[5] == 1:
                self.controller.motor_rotate(-30)
            else:
                self.controller.motor_rotate(0)

            try:
                if self.joy.button[12] == 1 and not self.sensor_.buzzer_thread.is_alive():
                    self.sensor_.buzzer_thread.start()
                    self.sensor_.buzzer_thread.join()
            except RuntimeError:
                self.sensor_.buzzer_thread = threading.Thread(target=self.sensor_.Buzzers, daemon=True, name='Buzzer')

            if self.joy.button[6] == 1:
                self.compensator_.servo_control(135, 100)
            elif self.joy.button[7] == 1:
                self.compensator_.servo_control(45, 100)

            cmd_vel = [0.0] * 4

            if abs(self.joy.axis[1]) >= MIN_SENSITIVITY:
                endwise_cmd = -self.joy.axis[1] * SPEED_MAX_PER
            else:
                endwise_cmd = 0.0
            if abs(self.joy.axis[2]) >= MIN_SENSITIVITY:
                infeed_cmd = -self.joy.axis[2] * SPEED_MAX_ANG
            else:
                infeed_cmd = 0.0

            cmd_vel[0] = endwise_cmd - infeed_cmd
            cmd_vel[1] = endwise_cmd + infeed_cmd
            cmd_vel[2] = endwise_cmd - infeed_cmd
            cmd_vel[3] = endwise_cmd + infeed_cmd

            cmd_vel = speed_limit(cmd_vel)
            self.chassis_.move(cmd_vel)
            # print(sensor_.Groy_Sensor(2, 4))

            self.sensor_.Nixie_Tube(self.sensor_.Groy_Sensor(2, 4))

        print("over!!!")


def speed_limit(speeds):
    i = 0
    for speed in speeds:
        if speed > 50:
            speed = 50
        elif speed < -50:
            speed = -50
        speeds[i] = speed
        i = i + 1
    return speeds


class Serial:

    def __init__(self):
        portx = "/dev/ttyUSB0"
        if CONTROLLER == "mc601":
            bps = 380400
        elif CONTROLLER == "wobot":
            bps = 115200
        else:
            bps = 115200
        self.serial_ = serial.Serial(portx, int(bps), timeout=0.0005, parity=serial.PARITY_NONE, stopbits=1)

四、 不足之处

对于任务来讲,其中最大的问题应在于寻航任务控制过程由于串口通讯繁忙而不灵敏,使得其通讯频率降低,导致机器人最终实现效果呈现经常出现左右摆头的现象。从第一次验收与第二次验收结果都可以看出来。
对于机器人本身来讲,侧边摄像头是一个固定关节,以至于其只能对机器人单侧进行检测,并且刀片也是只能对单侧进行挥砍,这也是导致地图上的功能无法做完的原因之一。

五、 技术展望

针对于本小组设计的这款智能车,用很多改进的地方:
1.加长车体,使得车体空间增加。
2.加长刀片,使得同时兼顾左右方向,打击坏人。
3.侧边摄像头增加Z轴自由度,使得可以兼顾机器人左右方向的识别检测。
4.增加车体控制线程,使得机器人控制频率不受进程运行时间的干扰。并在线程中设立线程锁,使得多个线程之间不可以同时向驱动器写入数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/703216.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

9小时通关 黑马新教程发布,含重磅项目~

随着测试行业的蓬勃发展&#xff0c;对从业者的要求越来越高&#xff0c;自动化测试已经成为软件测试中一个重要组成部分&#xff0c;广泛应用于各行各业。甚至&#xff0c;在圈子中还流传着这样一句话&#xff1a;学好测试自动化&#xff0c;年薪30万不在话下&#xff01; 今…

Qt读写文件

一、界面 项目文件结构 样例文件 中芯国际近期做出了两个重要改变&#xff1a;第一个是调整财报披露方式&#xff0c;不再公布芯片制程的营收占比&#xff0c;而只公布晶圆尺寸的营收占比&#xff1b;第二个是撤消14nm工艺的官方展示&#xff0c;只有28nm、40nm及以上的芯片工…

LeNet基础

目录 1.LeNet简介 1.1基本介绍 1.2网络结构 2.LetNet在pytorch中的使用 2.1首先定义模型 2.2初始化数据集&#xff0c;初始化模型&#xff0c;同时训练数据。 2.3 训练结果​编辑 2.4绘制曲线 1.LeNet简介 1.1基本介绍 LeNet&#xff08;LeNet-5&#xff09;是历史上第…

磁盘阵列(RAID)

什么是磁盘阵列 磁盘阵列&#xff08;RAID&#xff09;是一种将多个物理硬盘组合成一个逻辑存储单元的技术。这种技术可以提高数据存储的可靠性、性能或容量&#xff0c;并且可以在某些情况下提供备份和灾难恢复功能。 RAID技术可以通过在多个硬盘之间分配数据来提高性能。例…

事务处理相关

目录 步骤1.创建一个数据表 步骤2:创建项目导入jar包 步骤3:根据表创建模型类 步骤5:创建Service接口和实现类 步骤6:添加jdbc.properties文件 步骤7:创建JdbcConfig配置类 步骤8:创建MybatisConfig配置类 步骤9:创建SpringConfig配置类 步骤10:编写测试类 开启事务 1…

电磁阀原理精髓

一、引用 电磁阀在液/气路系统中&#xff0c;用来实现液路的通断或液流方向的改变&#xff0c;它一般具有一个可以在线圈电磁力驱动下滑动的阀芯&#xff0c;阀芯在不同的位置时&#xff0c;电磁阀的通路也就不同。 阀芯在线圈不通电时处在甲位置&#xff0c;在线圈通电时处在…

算法与数据结构-链表

文章目录 链表和数组的区别常见的链表类型单链表循环链表双向链表 总结 链表和数组的区别 相比数组&#xff0c;链表是一种稍微复杂一点的数据结构。对于初学者来说&#xff0c;掌握起来也要比数组稍难一些。这两个非常基础、非常常用的数据结构&#xff0c;我们常常会放到一块…

Python基础 - global nonlocal

global global作为全局变量的标识符&#xff0c;修饰变量后标识该变量是全局变量 global关键字可以用在任何地方&#xff0c;包括最上层函数中和嵌套函数中 实例1&#xff1a;如下代码&#xff0c;定义了两个x&#xff0c;并且赋值不同 直接调用print(x) 打印的是全局变量x的…

号外!MyEclipse 2023.1.1已发布,更好支持Vue框架

MyEclipse 2023.1.1是之前发布的2023.1.0的一个小错误修复版本&#xff0c;如果您已经安装了MyEclipse 2023&#xff0c;只需检查产品中的更新 (Help > Check for Updates…) 就可以选择这个新版本。或者&#xff0c;下载我们更新的离线安装程序来安装2023.1.1。 MyEclipse…

C# WPF应用使用visual studio的安装程序类的一些坑

重写installer实现自定义安装程序时&#xff0c;项目类型要选择 类库(.NET Framework) 否则会出现命名空间System.Configuration不存在Install的报错 有些可能想实现安装完自动启动应用的功能&#xff0c;就需要获取installer安装路径 var s Context.Parameters["assem…

【Java】网络编程与Socket套接字、UDP编程和TCP编程实现客户端和服务端通信

网络编程客户端和服务器Socket套接字流套接字TCP数据报套接字UDP对比TCP与UDP UDP编程DatagramSocket构造方法:普通方法&#xff1a; DatagramPacket构造方法:普通方法&#xff1a; 实现 TCP编程ServerSocket构造方法普通方法 Socket构造方法普通方法 实现 网络编程 为什么需要…

MyBatis-Plus 实现PostgreSQL数据库jsonb类型的保存

文章目录 在 handle 包下新建Jsonb处理类方式一方式二 PostgreSQL jsonb类型示例新建数据库表含有jsonb类型创建实体类创建Control 发起请求 在 handle 包下新建Jsonb处理类 方式一 import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFea…

低代码开发平台到底省掉了哪些成本?可能大家一直错了

低代码到底是否真正可以降低研发成本&#xff1f;是否每个团队都适合&#xff1f;如果能降低&#xff0c;到底是降低的什么成本&#xff1f;其实我觉得这个是我们每个技术交付团队应该在使用任何产品之前都要考虑的问题。 在我们考虑低代码是否能降低成本的问题前&#xff0c;…

【Python】一文带你学会数据结构中的字典、集合

作者主页&#xff1a;爱笑的男孩。的博客_CSDN博客-深度学习,活动,python领域博主爱笑的男孩。擅长深度学习,活动,python,等方面的知识,爱笑的男孩。关注算法,python,计算机视觉,图像处理,深度学习,pytorch,神经网络,opencv领域.https://blog.csdn.net/Code_and516?typeblog个…

一步一步学OAK之九:通过OAK相机实现视频帧旋转

目录 Setup 1: 创建文件Setup 2: 安装依赖Setup 3: 导入需要的包Setup 4: 定义变量Setup 5: 定义旋转矩形的四个顶点坐标Setup 6: 创建pipelineSetup 7: 创建节点Setup 8: 设置属性Setup 9: 建立链接Setup 10: 连接设备并启动管道Setup 11: 创建与DepthAI设备通信的输入队列和输…

C#核心知识回顾——2.拓展方法、运算符重载、分部类、里氏替换

1.拓展方法 为现有非静态变量类型添加新方法 1.提升程序拓展性 2.不需要再对象中重新写方法 3.不需要继承来添加方法 4.为别人封装的类型写额外的方法 特点&#xff1a; 1.一定是写在静态类中 2.一定是个静态函数 3.第一个参数为拓展目标 4.第一个参数用this修饰 /// <sum…

element table表格支持添加编辑校验

实现效果&#xff1a; 将table表格与form表单结合使用 &#xff08;用el-form外层包裹el-table结合rules进行校验&#xff09; 代码实现 <template><div><el-card class"box-card" shadow"never"><div><el-buttonsize"m…

E8-事关明细表里的控件事件绑定、日期的计算、明细表的求和等问题的处理办法

起因 下面的讲述的事情是从开发出差申请流程开始的。涉及的知识点偏多&#xff0c;且得容我慢慢梳理出来。以下篇幅可能会有点儿长&#xff0c;但内容我会争取写得精彩的。 图1 发起表单样式如图1&#xff0c;我想实现的是当修改出发日期或结束日期的时候&#xff0c;自动计算…

并发-synchronized详解

JDK1.6之前的synchronized关键字一来就直接给对象加了一把重量级锁&#xff0c;频繁地在用户态和内核态之间切换&#xff0c;导致性能非常低。为了弥补synchronized的不足&#xff0c;大佬doug lee写了一个AQS框架&#xff0c;用Java语言实现了ReentrantLock。然后在JDK1.6之后…

电脑文件夹怎么设置密码?3个方法为文件加密!

我的电脑里存了很多重要的文件夹&#xff0c;为了防止信息的泄露&#xff0c;我想把这些文件夹都设置密码。但是不知道具体应该如何操作。请求大家的帮助&#xff01; 我们平常在使用电脑时&#xff0c;可能会将很多重要的文件保存在电脑中。如果不想让别人看到我们这些重要的文…