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

news2024/12/28 20:30:32

一、项目简介

本项目现已基于鲸鱼机器人开发套件对其整体外形进行设计,并且对应于实习内容——以“丝绸之路”为题,对机器人各个功能与机器人结构部分进行相关设计与调整。主要可以实现“车道线巡检”“音乐交际”、“城堡检测”、“翻山越岭”。

本项目通过 Python的Serial 串口通讯库实现与机器人下位机的RS232通讯;通过Struct二进制解析库完成对于手柄IO数据口数据的解析,从而实现通过Xbox手柄控制机器人各个关节及其相应的功能;通过飞浆团队研发的轻量化深度学习推理框架——PaddleLite,实现对于车道拐弯线与机器人航偏值的预测、不同城堡与友谊交际数据的识别分类、不同地标数据和挡板的识别分类,进而通过前置摄像头作为机器人系统的感知器检测机器人前方的车道曲线、地标和挡板,通过左置摄像头感知器检测机器人左方的不同城堡等数据。进而使得本次实习通过上述三种框架实现了机器人的相关任务的完成。

相关资料
2022智能车百度创意组学习资料
链接:https://pan.baidu.com/s/1GjCP3fz_cikNNPMaSAWANQ
提取码:2022

二、实现原理

2.1RS-232协议

RS-232协议是美国电子工业协会EIA(Electronic IndustryAssociation)制定的一种异步串行通信物理接口标准,RS232协议的最新版本为RS-232-C。RS-232-C总线标准设有25 条信号线,包括一个主通道和一个辅助通道。在多数情况下主要使用主通道,对于一般双工通信,仅需三条信号线就可实现:发送线、接收线、地线。适合于数据传输速率在0~20kbps、传输距离小于20m的异步串行通信。
在异步串行通信中,通信协议重要包括:起始位、数据位、校验位、停止位、波特率。通信线上没有数据时处于逻辑1状态,当准备发送一个字符数据时,首先发送一个逻辑0,这个逻辑0就是起始位;在起始位后紧跟的是数据位,数据位的个数可以是5、6、7或8。校验一般采用奇偶检验,校验位可以不发送。校验位之后为停止位,停止位表示一个字符数据的结束,停止位可以是1位、1.5位或2位高电平。
与传统RS232接口不同的是,本次项目中的RS232采用的特制网口,需要接到MC601控制板,通过板子与上位机连接。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.2 pyserial库

Python的serial库是用于串口通信的库,它提供了一组与串口设备通信的函数和类。在使用serial 库进行RS232通讯时,首先,需要创建一个Serial对象来表示串口连接。创建Serial对象时需要指定串口号、波特率、数据位、停止位等参数。使用bytes的fromhex方法将十六进制的字符串格式协议转换为计算机可以直接读的二进制数据,使用Serial对象的write()方法向串口设备发送数据。使用Serial对象的read()或readline()方法从串口设备接收数据。read()方法可以指定要接收的数据长度,而readline()方法则会读取一行数据。使用Serial对象的close()方法关闭串口连接。使用Serial对象的flush()方法将缓冲区的数据全部发送出去并清空缓冲区。最后在操作完毕后,需要及时关闭串口连接,以释放串口资源。

三、实现过程

3.1基础搭建

参考基础版教程对智能车底盘进行组装,并安装控制板,参考官方文档机械臂、夹爪,独立设计两个相机的安装、举旗结构、击打坏人结构,最终智能车模型如下:
在这里插入图片描述

智能车搭建完成以后,对控制板进行环境搭建,参考文档将paddle_install放到指定文件夹执行.sh文件,安装paddlelite。环境搭建好以后便是通过电脑进行控制,将智能车的网口经过网线转USB接到电脑上,在网络适配器中找到新增加的连接进行更改,选择IPv4,

改成下图所示:

在这里插入图片描述

然后便可以通过安装的软件对智能车进行控制

在这里插入图片描述

Xftp和winscp可以进行文件传输,shell可以进行终端指令控制,鲸鱼编程软件可以单独控制硬件和传感器,MobaXterm_Personal既可以进行文件传输又可以进行终端控制。
以MobaXterm_Personal软件为例,点击sesson,选择SFTP,输入IP192.168.1.254,username为root,进入后输入密码root。如下图

在这里插入图片描述

连接后将官方提供的功能包src上传到板子,进入到workspace下,再通过ssh连接,执行python3 walk_lane.py,即可使用官方模型进行巡线,加前缀nohup可以断开网线进行巡线,按2开始,按4结束。将手柄接收器接到EdgeBoard板子USB口上,进入collect文件夹下,执nohup python3 collect_road_data.py,按手柄上的select后,蜂鸣笛响一下,即可手柄控制智能车跑巡线采集数据集,跑完之后按select键退出。之后将EdgeBoard板子collect下的train文件夹传到电脑,将训练数据集程序train_lane在pycharm新建项目,将train文件夹放到train_lane/train_data下,安装paddlepaddle_gpu执行cnn_trainer.py进行模型训练,如下图

在这里插入图片描述

3.2 各硬件传感器控制

3.2.1 简单程序实例

ser = serial.Serial("com3",380400, timeout=0.5)
data= '77 68 06 00 02 0C 01 02 20 0a'
cmd_data = bytes.fromhex(data)
ser.write(cmd_data)

通过python的serial库控制传感器,com3是传感器端口号,380400为波特率,06表示数据长度,00协议版本,02设置动作,0C为编码电机的编号,即10进制的12,01为版号,02为端口,表示电机接在拨号为01的电机控制板的M2口,20表示速度为32。

3.2.2 底盘控制

ChassisController类中move()方法需要传入一个含四个参数的列表与底层进行协议通讯控制四个电机的速度,run()和stop()方法用于识别到友谊时停车到指定位置,turn_left()方法,以指定速度左转,左右边轮速度互为相反数,turn_left()方法同理。steer()方法用于巡线,angle为识别到的线路阈值,将angle乘0.85,在delta正负不同时以1为基准进行左右轮差速实现转弯。

class ChassisController:
    def __init__(self, _serial: serial.Serial):
        self.serial = _serial
        self.speed = 25
        self.kx = 0.85
        self.min_speed = 20
        self.comma_head_all_motor = bytes.fromhex('77 68 0c 00 02 7a 01')
        self.comma_trail = bytes.fromhex('0A')
    def steer(self, angle):
        # print(angle)
        speed = int(self.speed)
        delta = angle * self.kx
        left_wheel = speed
        right_wheel = speed

        if delta < 0:
            left_wheel = int((1 + delta) * speed)
        elif delta > 0:
            right_wheel = int((1 - delta) * speed)
        # print("left_speed:", left_wheel, "  right_speed:", right_wheel)
        self.Move([left_wheel, right_wheel, left_wheel, right_wheel])

    def run(self, l_speed, r_speed):
        self.Move([l_speed, r_speed, l_speed, r_speed])

    def stop(self):
        self.move([0, 0, 0, 0])

    def move(self, speeds):
        left_front = int(speeds[0])
        right_front = -int(speeds[1])
        left_rear = int(speeds[2])
        right_rear = -int(speeds[3])
        self.min_speed = int(min(speeds))
        # print(speeds)
        left_front_kl = bytes.fromhex('01') + left_front.to_bytes(1, byteorder='big', signed=True)
        right_front_kl = bytes.fromhex('02') + right_front.to_bytes(1, byteorder='big', signed=True)
        left_rear_kl = bytes.fromhex('03') + left_rear.to_bytes(1, byteorder='big', signed=True)
        right_rear_kl = bytes.fromhex('04') + right_rear.to_bytes(1, byteorder='big', signed=True)
        send_data_all_motor = (self.comma_head_all_motor + left_front_kl + right_front_kl + left_rear_kl + right_rear_kl
                               + self.comma_trail)
        self.serial.write(send_data_all_motor)
        time.sleep(0.01)

    def turn_left(self):
        speed = self.speed
        left_wheel = -speed
        right_wheel = speed
        self.move([left_wheel, right_wheel, left_wheel, right_wheel])

    def turn_right(self):
        speed = self.speed
        left_wheel = speed
        right_wheel = -speed

        self.move([left_wheel, right_wheel, left_wheel, right_wheel])

    def reverse(self):
        speed = self.speed
        self.move([-speed, -speed, -speed, -speed])

3.2.3 RGB灯光闪烁

rgb_light类定义了2维列表light_num,分别向第一个灯、第二个灯、第三个灯、第四个灯,写入白,红、绿、蓝灯光,实现四个灯依次亮,转完一圈后变颜色继续转的闪烁效果。之后定义线程,使程序运行时灯光一直闪烁。

class rgb_light:
    def __init__(self, ser: serial.Serial):
        self.stop_event = threading.Event()  # 初始化 stop_event 属性
        self.ser = ser

    def light_control(self):
        light_num =[['01 ff ff ff','02 ff ff ff','03 ff ff ff','04 ff ff ff'],
                    ['01 00 ff ff','02 00 ff ff','03 00 ff ff','04 00 ff ff'],
                    ['01 ff 00 ff','02 ff 00 ff','03 ff 00 ff','04 ff 00 ff'],
                    ['01 ff ff 00','02 ff ff 00','03 ff ff 00','04 ff ff 00'],]
        while not self.stop_event.is_set():
            for j in range(len(light_num)):
                for i in range(len(light_num[0])):
                    cmd_data = bytes.fromhex(data) + bytes.fromhex(light_num[j][i]) + bytes.fromhex('0A')
                    self.ser.write(cmd_data)
                    time.sleep(0.5)
            self.ser.write(bytes.fromhex(data1))

if __name__ == '__main__':
    ser = serial.Serial("com5", 380400, timeout=0.5)
    data = '77 68 08 00 02 3B 04'  # 灯光 04 00 00 00
    rgb_light = rgb_light(ser)
    thread = threading.Thread(target=rgb_light.light_control)
    thread.start()
    time.sleep(20)
    rgb_light.stop_event.set()  # 调用 stop_event 对象的 set 方法来停止线程

3.2.4 举旗

向RaiseFlag类的servo_control方法传入角度和速度两个参数,即可控制智能舵机转动的角度和速度,self.angle里面定义了几个常用角度。

class RaiseFlag:

    def __init__(self, _serial: serial.Serial):
        self.serial = _serial
        self.raise_flag = bytes.fromhex('77 68 08 00 02 36 02')
        self.comma_trail = bytes.fromhex('0A')
        self.angle = [225, 135, 45, -45]

    def servo_control(self, angle, speed):
        angle = int(angle)
        cmd_servo_data = self.raise_flag + speed.to_bytes(1, byteorder='big',
                                                          signed=True) + angle.to_bytes(3, byteorder='little',
                                                                                        signed=True) + bytes.fromhex(
            '0A')

        self.serial.write(cmd_servo_data)
        self.serial.flush()
        time.sleep(0.01)

3.2.5 数码管显示陀螺仪角度、蜂鸣器唱歌

SensorController类中Buzzers()方法向蜂鸣器写入列表buzzer_list中的每一组包含音高和音长的参数,根据1-9个等级的音高结合音长粗略模仿D0、Re、Mi、Fa、So、La、Ti七个基本音符实现小星星歌曲音律效果。Groy_Sensor()方法传入板号和陀螺仪连接端口号即可控制陀螺仪,获取当前机械臂倾斜角度值,并用if,else语句排除了有时可能出现的异常。Nixie_Tube()方法需要传入陀螺仪的检测角度值,对值进行处理实现机器人学坐标系下的机械臂倾斜角度显示,即机械臂水平向上角度从360递减,机械臂水平向下角度从0递增。最后析构函数调用Nixie_clear()方法清除数码管显示。

class SensorController:

    def __init__(self, _serial: serial.Serial):
        self.serial = _serial
        self.nixie_tube = bytes.fromhex('77 68 06 00 02 38 02')
        self.nixie_clear = bytes.fromhex('77 68 06 00 02 39 02')
        self.buzzer = bytes.fromhex('77 68 06 00 02 3D 00 01 0A')
        self.buzzer_list = [[1, 2], [1, 2], [5, 2], [5, 2], [6, 2], [6, 2], [4, 4], [4, 2], [4, 2], [3, 2], [3, 2],
                            [2, 2],
                            [2, 2], [1, 4]]
        self.buzzer_thread = threading.Thread(target=self.Buzzers, daemon=True, name='Buzzer')
        self.comma_trail = bytes.fromhex('0A')
        self.done = True
    def Buzzers(self):
        for say in self.buzzer_list:
            say_str = '{:02x}'.format(say[0])
            tm_str = '{:02x}'.format(say[1])
            # print(say_str, '--', tm_str)
            cmd_data = bytes.fromhex('77 68 06 00 02 3D {} {} 0A'.format(say_str, tm_str))
            lock.acquire()
            self.serial.write(cmd_data)
            self.serial.flush()
            lock.release()
            time.sleep(say[1] / 4)

    def Groy_Sensor(self, value, port):
        value_str = '{:02x}'.format(value)
        port_str = '{:02x}'.format(port)
        cmd_data = bytes.fromhex('77 68 06 00 01 DC {} {} 0A'.format(port_str, value_str))
        self.serial.write(cmd_data)
        self.serial.flush()
        time.sleep(0.01)
        read_data = self.serial.readline()
        if len(read_data) < 11 or read_data[-3] != port or read_data[-4] != 0xDC:
            return None
        else:
            data = read_data[3: (3 + 4)]
            sensor = struct.unpack('<i', struct.pack('4B', *data))[0]
            return sensor
    def Nixie_Tube(self, value):
        try:
            if value < 0:
                value = 360 + value
            else:
                value = value
            value = (value & 0xff).to_bytes(1, byteorder='big', signed=True) + (value >> 8).to_bytes(1, byteorder='big',
                                                                                                     signed=True)
        except Exception as err:
            print(err)
            value = 0
            value = (value & 0xff).to_bytes(1, byteorder='big', signed=True) + (value >> 8).to_bytes(1, byteorder='big',
                                                                                                     signed=True)

        cmd_data = self.nixie_tube + value + self.comma_trail
        self.serial.write(cmd_data)
        self.serial.flush()
        time.sleep(0.01)

    def Nixie_clear(self):
        cmd_data = self.nixie_clear + bytes.fromhex('00 00') + self.comma_trail
        self.serial.write(cmd_data)
        self.serial.flush()
        time.sleep(0.01)

    def __del__(self):
        self.Nixie_clear()

四、实现结果

最终巡航完成了“车道线巡检”、“翻山越岭”,举旗,在检测到friendship 播放歌曲小星星完成“音乐交际”,巡航依然有问题尚未解决。
手柄控制效果视频链接

点赞+收藏+关注后私信博主获取完整代码

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

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

相关文章

Java使用策略模式和工厂模式来消除冗余的if-else语句(UML类图+案例+提供Gitee源码)

前言&#xff1a;在最近的后端开发中&#xff0c;多多少少会发现有很多if-else语句&#xff0c;如果条件过多则会造成整体代码看起来非常臃肿&#xff0c;这边我就举一个我在实际开发中的例子&#xff0c;来进行阐述这两种模式在实际开发中我是如何运用的。 目录 一、工厂模式…

把Jar打包为Maven 把jar打包为maven 将java项目打包为maven 将Java项目打包为Maven

把Jar打包为Maven 把jar打包为maven 将java项目打包为maven 将Java项目打包为Maven 自己写了一个通用SDK Jar包&#xff0c;但是现在的项目都是Maven项目&#xff0c;需要把Jar打包为Maven格式&#xff0c;输出到本地Maven仓库&#xff0c;在项目中可以引用查看Maven是否安装打…

nginx进行反向代理

Nginx是一个开源的高性能Web服务器和反向代理服务器。它最初是由Igor Sysoev在2004年开发的&#xff0c;现在由一个全球性的社区维护和支持。 Nginx的主要特点包括&#xff1a; 高性能&#xff1a;Nginx使用事件驱动模型&#xff0c;可以处理高并发请求&#xff0c;具有出色的…

A* 算法研究(附 Python / C++ 实现)

A* 算法研究 参考 A*寻路算法详解 #A星 #启发式搜索 路径规划之 A* 算法 最短路搜索-从Dijkstra到Best-First再到A-Star 路径规划算法学习笔记&#xff08;一&#xff09;&#xff1a;A*算法 A*算法寻路&#xff08;C代码实现&#xff09; 《基于A*算法的自动泊车全局路径规划…

恒生电子联合恒生聚源发布数智金融新品,聚焦大模型技术金融业务应用

6月28日&#xff0c;恒生电子和旗下子公司恒生聚源正式发布基于大语言模型技术打造的数智金融新品&#xff0c;金融智能助手光子和全新升级的智能投研平台WarrenQ。此外&#xff0c;恒生电子金融行业大模型LightGPT也首次对外亮相&#xff0c;并公布最新研发进展。 恒生电子董…

升级Win10后多了个恢复分区,有什么用

很多用户从Win7/Win8/Win8.1升级到Win10之后发现电脑硬盘上多出了一个“恢复分区”&#xff0c;64位系统下这个分区大小在450MB左右。那么为什么会多出这样一个分区&#xff0c;这个分区又是干什么的&#xff0c;能不能删除呢&#xff1f;下面以MBR硬盘情况为例来说明。 1.全盘…

MySQL数据库 SQL语言命令总结 数据类型、运算符和聚合函数汇总

数据库&#xff1a;存储数据的仓库&#xff0c;有组织的进行存储数据。SQL&#xff1a;操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库统一标准。常用的关系型数据库管理系统&#xff1a;Oracle、MySQL、Microsoft SQL Server等。 Oracle是大型收费数据库&…

初识express/路由/中间件

路由的概念 模块化路由 中间件(要有输入输出) 简化版本 全局生效中间件 局部生效中间件 注意事项 中间件分类 内置中间件,解析请求体/url-encoded 自定义中间件 使用querystring模块解析请求体数据 编写接口 ​​​​​​​

x86_64(intel64、amd64)和ARM64的区别以及发展

文章目录 区别引用 区别 ARM64架构 ARM 公司研发的&#xff0c;用的是精简指令集&#xff08;追求节能&#xff0c;低功耗&#xff09;。通常用于手机、平板等CPU&#xff0c;目前笔记本电脑也会采用ARM64构架的CPU&#xff0c;比如mac m1就是arm64(查看命令&#xff1a;uname…

智能佳—LoCoBot WX250 6自由度

&#xff08;用于科研与教学的ROS智能车&#xff09; LoCoBot是用于映射、导航和操纵&#xff08;可选&#xff09;等ROS研究的智能车&#xff0c;研究人员、教育工作者和学生都可以使用LoCoBot专注于高级代码的开发&#xff0c;而不是专注硬件和构建低级代码。通过开放的源代码…

EasyExcel实现指定列自定义导出

效果展示 全部导出 自定义导出 代码实现 1.引入依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.1</version> </dependency> 2.实体类 Data public class User {Exc…

vue PC端完成电子签名

最近接到一任务&#xff0c;有一个功能&#xff0c;重来没有遇到过。就是电子签名 看了原型其他基本都是对接口、写表单&#xff0c;难度不大&#xff0c;先把电子签名给攻克了起。 因为项目是vue 所有使用了 vue-esign 组件 1. 安装依赖 npm install vue-esign --save2.使用…

C++中的关联容器map下标运算符[]使用分析

最近使用到C中的map&#xff0c;发现一个问题&#xff0c;如果一个键不存在时&#xff0c;下标运算符会创建一个新的元素&#xff0c;其关键字为键。 一&#xff0c;问题重现 首先看一下问题描述&#xff1a; 本题要求读入 N 名学生的成绩&#xff0c;将获得某一给定分数的学生…

最细接口自动化测试yaml框架,超全详解,一篇打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 YAML文件介绍 YAM…

津津乐道设计模式 - 责任链模式详解(教你更优雅的处理商城下单业务)

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

Linux系统的目录结构与基本命令

目录 Linux系统使用注意 Linux严格区分大小写 Linux文件"扩展名" Linux系统中常见的后缀名称&#xff1a; Linux中所有内容以文件形式保存 Linux中存储设备都必须在挂载之后才能使用 Linux系统的目录结构 Linux分区与Windows分区 Linux系统文件架构 Linux系…

用AI帮助小学一年级记住常用字

这几天娃就要一年级毕业放假了&#xff0c;感觉时间过得太快了。再开学就要上二年级&#xff0c;可汉字表上的区区三四百字&#xff0c;咋就那么的难读&#xff1f;难记&#xff1f;喊他们来认字&#xff0c;拉都拉不过来。哎&#xff0c;愁啊&#xff0c;替他们焦虑。突发奇想…

Docker发布VUE vhr微人事前端(Nginx 403 forbidden)

本文代码来源于&#xff08;感谢作者&#xff09; GitHub - lenve/vhr: 微人事是一个前后端分离的人力资源管理系统&#xff0c;项目采用SpringBootVue开发。 发布过程参考博主 【Docker】使用docker容器发布vue项目_docker 发布vue_TOP灬小朋友的博客-CSDN博客 1.创建Do…

食品空压机数据采集远程监控系统解决方案

食品行业是一个需求量大、安全标准高、竞争激烈的行业。随着人们消费水平的提升&#xff0c;对食品的品质、口味、营养、卫生等方面有了更高的要求。食品空压机是食品生产过程中不可缺少的设备&#xff0c;它可以提供稳定的压缩空气&#xff0c;用于食品加工、包装、运输等环节…

大聪明教你学Java | 深入浅出聊 ThreadPoolExecutor

前言 🍊作者简介: 不肯过江东丶,一个来自二线城市的程序员,致力于用“猥琐”办法解决繁琐问题,让复杂的问题变得通俗易懂。 🍊支持作者: 点赞👍、关注💖、留言💌~ 在《阿里巴巴 Java 开发手册》中有这么一个强制要求:“线程池不允许使用 Executors 去创建,而是…