机器谱

S129】基于模块化机器人的全地形月球车

图文展示3264(1)

图文展示3264(1)

副标题

机械结构设计
控制系统设计

作者:杨森 冯仕成 谭伟

单位:重庆工商大学

指导老师:李永亮 陈蕙妃

1. 市场调研及市场前景说明

      全地形探测车是现代星球探索而必不可少的探测车,而月球车则是月球探索与开发必不可少的科技产品,随着科学技术的不断发展,月球车也在不断的被完善中。国内外的许多科学家也竞相踏上了开发之路,但是目前的月球车仍然不是十分的智能与完善,在此道路上,人类还有更远的路需要走,宇宙中的资源还亟待我们去开发,当地球资源用尽之时,就是人类踏上星球开发之路之始。本项目在对各国科学家的研究成果之上,简要评析了总体研究现状例如月球车机械结构、定位技术、避障与越障、图像传输和机械手采集样件等研究,并指出当前探索所遇到的问题以及未来研究的趋势为该领域的进一步研究提供参考。

      月球车是一项技术复杂、要求严格的研究开发任务,开发者除了要突破、掌握同机器人相关的轻型机械、机构、遥感操作、自主导航和机械臂等技术外,更重要的是要在按航天器的规范与标准研制管理上多下工夫。所以针对本项目的相关技术,做了全面的市场调研。

1.1 对于月球车机械结构的设计研究

      月球车机械结构主要包括轮式行走机构、传动机构等。月球车在月球的探索开发功能主要研究在于轮式的研究。行星探测车包括不局限于月球车,在探测车的发展历史中,曾经出现过单轮、三轮、四轮、五轮和多轮等不同结构的探测车,但单轮、三轮平稳性差,四轮负载能力较小,五轮越障能力和通过性受到移动系统方向的限制,都被历史所抛弃,目前研究最多的即是六轮驱动系统,其没有较为明显且无法为科学所接受的缺陷。

1.2 月球车的历史研究与发展

      1970年11月17日苏联“月球”17号探测器把世界上第一个无人驾驶的“月球车”1号送上月球,它行驶了10.5公里,考察了8万平方米的月面。后来的“月球车”2号行驶了37公里,向地球发回88幅月面全景图。1971年7月31日,“阿波罗15”号宇航员戴维斯-R-斯科特和詹姆斯-B-欧文进行了人类首次月球车行驶,他们驾驶着4轮月球车,在崎岖不平的月球表面上,越过陨石坑和砾石行驶了数公里。斯科特和欧文成为在月球上漫步的第7位和第8位人,而且是第一个在月球上驾车行驶的。他们于30日在月球的“雨海”登陆,并于美国东部时间31日上午9时25分离开“隼”号登月舱。几分钟之后,他们从宇宙飞 船上卸下月行车,开始了他们的勘探旅行。游车的前舵轮操作不灵,但是按设计只有后轮驱动,后驱动轮运转良好。

      当宇航员们在埃尔鲍陨石坑的边沿停下时,位于休斯顿的任务控制台打开了游车的电视摄影机,向地球传送非常清晰的彩色 图像。电视观众可以看到宇航员挑选和采集月石标本。有一次,他们兴奋地喊道,“这里有些漂亮的供地质研究用的岩石。”他们驾车行驶了两小时,走了8公里,之后又回到登月舱。 按计划,斯科特和欧文将在后两天驾驶月行车做更多的旅行。他们将同在指挥船中的另一名“阿波罗15”号宇航员阿尔弗雷德- M-沃顿会合,一起返回。2019年1月3日,嫦娥四号任务月球车名字揭晓,月球车命名为“玉兔二号”;1月3日10时26分成功着陆月球背面,随即着陆器与巡视器分离,开始就位探测和巡视探测。

1.3 对于月球车的定位技术的研究

      月球车离开地球飞到月球进行探索,所以我们应该实时了解其位置,这样地面才能更好的进行指挥开发,国内外的许多专家对此进行了大量的研究,其中罗志强于2007年在其月球车定位系统及其定位信号处理一文中提到月球车的基本定位原理,即地面基站与月球车各有一套收发系统,月球车的收发模块中有两根天线,一根用来接收基站的发射信号,其中包括控制信号及测距信号,然后通过另一根信号线处理后发射出去;而基站的收发模块则有四根天线,一根信号用来发射月球车接收到的信号,另外三根信号线用来接收月球车发送的信号电磁波。那么两者之间的通信则是通过串口数据进行首发于通信,其基本原理为发射机发送定位脉冲给月球车的FPGA和单片机,使能FPGA进行计数,与此同时,单片机开始进行AD采样。待到采样结束,其单片机将数据发送回地球基站。

1.4 对于月球车的障碍识别与越障前行的研究

      所周知,月球的环境相较地球而言是十分恶劣的,不利于人类居住,其主要有以下几点问题,即是重力、路况、温度、强辐射等,那么对于月球车而言,其前行需要考虑到重力路况等,然而月球重力是地球的1/6,那便意味着,质量为50千克的东西,在地球上所受重力约500牛。到了月球表面则变成约80牛。因此,月球表面的土壤非常松软,月球车的行进效率会降低。故我们需要考虑到其行进方式,如轮式足式履带式等,一般而言,多采用轮式加加摇臂行进。宁杭于2016年在其硕士毕业论文六轮摇臂摆杆式行星探测车的结构设计与分析一文中提到前苏联研制的火星探测车火星探测车地球样机Marsokhod的结构中具有12个自由度,越障能力高,能越过0.5m的障碍物,其越障能力较高。而我国研制的月球探测车嫦娥三号采用六轮驱动,其越障性能较好,能越过0.2m高的障碍物,爬20°的斜坡。可见,虽然在越障领域,各国月球车的越障性能较为出色,但是仍然还有许多的提升空间与研究价值。而障碍识别的准确性直接关乎到月球车的探测效率,如果月球车能自主识别障碍物并越障前行,这将大大提高其工作效率。一般而言,月球车的障碍识别系统由控制单元、力觉子系统、视觉子系统、决策系统与数据库等组成,首先通过力觉子系统和视觉子系统中的近距相机获取近距地形的力学特征与文理特征,并将其存入数据库,同时获取远距地形的特征,对比数据库,通过决策系统判断出远距地形的可穿越性。

1.5 对于月球车机械手采集样件的研究

      月球探测车的总目标既是勘探月球的环境并采集月球土壤样本,并将其带回地球供人类研究使用,所以样件采集功能是必不可少的,目前各国科学家的研究主要针对的是铲土,机械手采集等,其中最为广泛的是机械手采集,机械手采集样件具有可操作性高,便捷的优点,但是对于研发的要求也较高。在采集过程中,应保持样本的完整性,不可破坏其结构,一旦破坏结构,必然导致研究偏差,这样研究的意义将不是很大。马亮于2015年在其深层采样全过程月壤样品层理保持特性分析的硕士毕业论文中提到,基于离散元方法建立月壤颗粒接触碰撞三维模型,在传统Hertz模型基础上考虑月壤颗粒间弯曲,扭转力矩作用,同时对月壤颗粒间存在的静电引力、范德华力等场作用力进行等效,提出弹簧等效引力作用并设定最大引力失效区,得到考虑等效引力的颗粒间法向、切向接触力及弯曲、扭转力矩计算方法,有效避免了对于样件的破坏。

1.6 对于月球车图像传输的研究

      图像传输功能在星球探测车中同样是必需的。图像传输由图像采集、压缩编码、通信和解码等几个独立模块实现。对于研发针对性强、界面简洁、易于操作、稳定性好的专用视频图像传输手段迫在眉睫。小波编码是一种图像分解的重要手段,小波分解将信号分解为不同层次,每一层次的分辨率不同。由于小波分解方法本身的正交性,分解后不同层次数据之间的 相关性完全由数据本身的相关性所决定。小波变换的本质是多分辨率或多尺度 的分析信号,非常适合视觉系统对频率感知的对数特性。因此,从本质上说小波变换非常适合于图像信号的处理。

1.7 总结

      由上述综述可见,人类从未停止过对于月球的研究,但是月球车的研究开发,我们还有很长的一段路要走。尽管对于月球车的障碍识别和越障前行、定位技术、机械手采集样件以及图像传输等都有了较为成熟的研究,但是还不能说是趋于完美,我们还需要更加的努力才能窥探到月球的全貌。未来对于月球,或者说不只是月球,可能是宇宙中其他的神秘星球,我们都将会做进一步的研究开发,但是就目前来说,我们首先要做的就是做好月球资源的提取与研究,宇宙正在等着我们。

2. 全地形月球车机械结构的设计

2.1 引言

      月球车设计中最主要的即是车体结构与传感器程序设计。而车体结构更是重中之重,只有完整可靠的车体结构才能为传感器安装与使用提供最好的环境,本章即是对于车体结构做一个初步的设计。

2.2 全地形月球车结构基本介绍

      全地形月球车是登月探索必不可少的工具,其中月球车结构可分为两轮并列式、行星轮式、摇臂-转向架式、六圆柱-圆锥轮型月球车。其中轮式还可分为两轮、四轮、五轮、六轮以及八轮月球车,目前普遍采用的是六轮式月球车。其具有稳定性好,自由度多等优点。

2.3 全地形月球车行走系统的选择2.3.1轮式行走系统

      项目选择六轮轮式行走加悬架系统作为研究对象,不仅由于轮式行走系统是目前主要的星球探测车机构,主要是因为轮式行走系统具有结构简单、便于实现、越障能力强、自由度高等特点,而且相对于履带式而言更加的易于控制。其结构如下图所示:

机械结构设计

六轮悬挂式系统机构简图

轮式行走系统的优点

      普通轮式行走常常采用包容结构,集成了线性牵引,转向驱动和检测等功能模块的设计和制造,降低了质量,提高了可靠性(。本文所提及的六轮悬挂式月球车兼具此类有点,且由于此结构是国际上普遍采用的月球车探月结构,其稳定性、实用性等都有较为良好的表现。

普通轮式

轮式行走系统的缺点

      与行星轮式相比,普通轮式在越障能力上较外行星轮式弱且外行星轮式并且它具有一定的地面自适应能力,但其转向只能通过差速实现,而不是像普通车轮那样灵活。也没有履带式行走系统的适应性,履带式行走系统能更好的适应沙地效果。如下图所示:

外行星轮式和履带式

2.3.2 全地形月球车与六轮悬挂系统的关系

      这里选择采用六轮悬挂式月球车作为模型搭建,此结构是月球车常用结构,其主要特点为:本文采用的模型使用六轮驱动,可根据不同的地形采用不同的驱动方式,可从两轮到六轮之间进行任意切换。如识别到可跨越障碍时,可采用六轮驱动方式,且前两轮速度较后面四轮快,这样可以帮助月球车能够顺利的通过障碍;同样,在较为平坦的道路行走之时,可以采用四轮驱动,即前两轮与后两轮驱动,中间两轮处于静止状态,这样可以节约电池消耗,能够最大限度的节约能源。具体可见下图:

轮式驱动结构

      在驱动运动的过程中,月球车的六轮悬挂系统是不处于同一平面的,也即是说在能够达到的越障高度的运动过程中,整车的重心可以保持在同一平面,这样做的好处有如下两点:

      可以更大的保持越障性能,在允许的范围内,理论上来说能够加大越障高度。

      可以保持重心的稳定,使月球车上的勘测物品不至于倾倒,也能够保持车体的稳定性能。

2.4 月球车机械结构
月球车机械结构图包含部分:

      月球车整体结构图

      月球车结构拆分图

      悬挂系统建模

整体结构图

整体结构拆分图

悬挂系统建模

#!/usr/bin/env python

# -*- coding:utf-8 -*-

#Author: WLHomework

import time

import RPi.GPIO as GPIO

# 定义端口

TRIG = 11

ECHO = 12


# 忽略警告

GPIO.setwarnings(False)

# 安装端口

def setup():

    GPIO.setmode(GPIO.BOARD)

    GPIO.setup(TRIG, GPIO.OUT)

    GPIO.setup(ECHO, GPIO.IN)

# 测距函数

def distance():

    GPIO.output(TRIG, 0)

    time.sleep(0.000002)

    GPIO.output(TRIG, 1)

    time.sleep(0.00001)

    GPIO.output(TRIG, 0)

    while GPIO.input(ECHO) == 0:

        a = 0

    time1 = time.time()

    while GPIO.input(ECHO) == 1:

        a = 1

    time2 = time.time()

    during = time2 - time1

return during * 340 / 2 * 100

# 循环测距

def loop():

    while True:

        dis = distance()

        print(dis, 'cm')

        print('')

        time.sleep(0.3)

    return dis

# 销毁GPIO端口

def destroy():

    GPIO.cleanup()




#!/usr/bin/env python

# -*- coding:utf-8 -*-

#Author: WLHomework

# 导入模块

from __future__ import division

import RPi.GPIO as GPIO

import time

import Adafruit_PCA9685


# 实例化PWM产生函数

pwm = Adafruit_PCA9685.PCA9685()

# 定义最大最小脉冲,其范围为0-4096

servo_min = 0                       # Min pulse length out of 4096

servo_max = 3000                    # Max pulse length out of 4096


# 定义端口

TrackPin1 = 13

TrackPin2   = 15


# 定义端口安装

def setup():

    GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical location

    #GPIO.setup(LedPin, GPIO.OUT)   # Set LedPin's mode is output

    GPIO.setup(TrackPin1, GPIO.IN, pull_up_down=GPIO.PUD_UP)

    GPIO.setup(TrackPin2, GPIO.IN, pull_up_down=GPIO.PUD_UP)

    #GPIO.output(LedPin, GPIO.HIGH) # Set LedPin high(+3.3V) to off led


#----------------------------前进函数---------------------------

# 参数与否:有两个参数,一个为最大脉冲,一个为最小脉冲

# 函数功能:根据所传递的参数以某速度前进

# 具体实现:脉冲脉宽调制

def forward(servo_max, servo_min):

    pwm.set_pwm(0, 0, servo_min)            # 左边第一个轮子

    pwm.set_pwm(1, 0, servo_max)           

    pwm.set_pwm(2, 0, servo_min)

    pwm.set_pwm(3, 0, servo_max)

    pwm.set_pwm(4, 0, servo_min)            # 左边第三个轮子

    pwm.set_pwm(5, 0, servo_max)           

   

    pwm.set_pwm(6, 0, servo_min)            # 右边第一个轮子

    pwm.set_pwm(7, 0, servo_max)           

    pwm.set_pwm(8, 0, servo_min)

    pwm.set_pwm(9, 0, servo_max)

    pwm.set_pwm(10, 0, servo_min)           # 右边第三个轮子

    pwm.set_pwm(11, 0, servo_max)

   

#----------------------------后退函数---------------------------

# 参数与否:有两个参数,一个为最大脉冲,一个为最小脉冲

# 函数功能:根据所传递的参数以某速度后退

# 具体实现:脉冲脉宽调制

def back(servo_max, servo_min):

    pwm.set_pwm(0, 0, servo_max)            # 左边第一个轮子

    pwm.set_pwm(1, 0, servo_min)           

    pwm.set_pwm(2, 0, servo_max)

    pwm.set_pwm(3, 0, servo_min)

    pwm.set_pwm(4, 0, servo_max)            # 左边第三个轮子

    pwm.set_pwm(5, 0, servo_min)           

   

    pwm.set_pwm(6, 0, servo_max)            # 右边第一个轮子

    pwm.set_pwm(7, 0, servo_min)           

    pwm.set_pwm(8, 0, servo_max)

    pwm.set_pwm(9, 0, servo_min)

    pwm.set_pwm(10, 0, servo_max)           # 右边第三个轮子

    pwm.set_pwm(11, 0, servo_min)   

   

#----------------------------前进函数---------------------------

# 参数与否:有两个参数,一个为最大脉冲,一个为最小脉冲

# 函数功能:根据所传递的参数以某速度左转

# 具体实现:脉冲脉宽调制

# 实现思路:左边三个轮子正转,右边三个轮子不转或者转的较慢

def left(servo_max, servo_min):

    pwm.set_pwm(0, 0, servo_min)            # 左边第一个轮子

    pwm.set_pwm(1, 0, servo_max)           

    pwm.set_pwm(2, 0, servo_min)

    pwm.set_pwm(3, 0, servo_max)

    pwm.set_pwm(4, 0, servo_min)            # 左边第三个轮子

    pwm.set_pwm(5, 0, servo_max)   

   

    pwm.set_pwm(6, 0, servo_max)            # 右边第一个轮子

    pwm.set_pwm(7, 0, servo_min)           

    pwm.set_pwm(8, 0, servo_max)

    pwm.set_pwm(9, 0, servo_min)

    pwm.set_pwm(10, 0, servo_max)           # 右边第三个轮子

    pwm.set_pwm(11, 0, servo_min)           


#----------------------------前进函数---------------------------

# 参数与否:有两个参数,一个为最大脉冲,一个为最小脉冲

# 函数功能:根据所传递的参数以某速度右转

# 具体实现:脉冲脉宽调制

# 实现思路:右边三个轮子正转,左边三个轮子不转或者转的较慢

def right(servo_max,servo_min):

    pwm.set_pwm(0, 0, servo_max)            # 左边第一个轮子

    pwm.set_pwm(1, 0, servo_min)           

    pwm.set_pwm(2, 0, servo_max)

    pwm.set_pwm(3, 0, servo_min)

    pwm.set_pwm(4, 0, servo_max)            # 左边第三个轮子

    pwm.set_pwm(5, 0, servo_min)   

   

    pwm.set_pwm(6, 0, servo_min)            # 右边第一个轮子

    pwm.set_pwm(7, 0, servo_max)           

    pwm.set_pwm(8, 0, servo_min)

    pwm.set_pwm(9, 0, servo_max)

    pwm.set_pwm(10, 0, servo_min)           # 右边第三个轮子

    pwm.set_pwm(11, 0, servo_max)   


def stop():

    pwm.set_pwm(0, 0, 0)            # 左边第一个轮子

    pwm.set_pwm(1, 0, 0)           

    pwm.set_pwm(2, 0, 0)

    pwm.set_pwm(3, 0, 0)

    pwm.set_pwm(4, 0, 0)            # 左边第三个轮子

    pwm.set_pwm(5, 0, 0)   

   

    pwm.set_pwm(6, 0, 0)            # 右边第一个轮子

    pwm.set_pwm(7, 0, 0)           

    pwm.set_pwm(8, 0, 0)

    pwm.set_pwm(9, 0, 0)

    pwm.set_pwm(10, 0, 0)           # 右边第三个轮子

    pwm.set_pwm(11, 0, 0)   


def loop():

    while True:

        if GPIO.input(TrackPin1) == GPIO.LOW and GPIO.input(TrackPin2) == GPIO.LOW:

            print 'White line is detected'

            forward(1500,0)

    #       GPIO.output(LedPin, GPIO.LOW)   # led on

        elif GPIO.input(TrackPin1) == GPIO.LOW and GPIO.input(TrackPin2) == GPIO.HIGH:

            print("White left,Black right")

            right(1500,0)

        elif GPIO.input(TrackPin1) == GPIO.HIGH and GPIO.input(TrackPin2) == GPIO.LOW:

            print("Black left,White right")

            left(1500,0)

        elif GPIO.input(TrackPin1) == GPIO.HIGH and GPIO.input(TrackPin2) == GPIO.HIGH:

            print('Black line is detected')

            stop()

    #       GPIO.output(LedPin, GPIO.HIGH) # led off


def destroy():

    #GPIO.output(LedPin, GPIO.HIGH)     # led off

    GPIO.cleanup()                     # Release resource


if __name__ == '__main__':     # Program start from here

    setup()

    try:

        loop()

    except KeyboardInterrupt:   # 当“Ctrl + C”按下的时候,程序终止

        stop()

        destroy()




#!/usr/bin/python

#-*-coding:utf-8 -*-

# 导入模块

import socket

# 定义端口

address = ('192.168.43.119', 8080)                                                   # 设置地址与端口,如果是接收任意ip对本服务器的连接,地址栏可空,但端口必须设置

# 定义套接字类

class ServerSocket(object):

    # 定义初始化函数

    def __init__(self):

        # socket.AF_INET用于服务器与服务器之间的网络通信

        # socket.SOCK_STREAM代表基于TCP的流式socket通信

        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self.server_socket.bind(address)                                        # 将Socket(套接字)绑定到地址

        self.server_socket.listen(True)                                         # 开始监听TCP传入连接

        print('Waiting for images...')

    # 定义图像接收函数

    def run(self):

        # 接受TCP链接并返回(conn, addr),其中conn是新的套接字对象,可以用来接收和发送数据,addr是链接客户端的地址。

        conn, addr = self.server_socket.accept()                                # 接收客户端的连接,并创建新的套接字

        print('Successful connect!')


        stringData = conn.recv(1024*2048)                                       # 接收树莓派发送过来的数据

        name = time.strftime('%Y-%m-%d-%S',time.localtime(time.time()))

        with open(name + '.jpg', 'wb') as f:                                        # 保存名为 采集.jpg 的图片。

            f.write(stringData)


        conn.send("Server has recieved messages!".encode('utf-8'))              # 回复树莓派,传输完成


        self.server_socket.close()                                              # 关闭套接字

# 主函数

if __name__ == '__main__':

    while True:

        s = ServerSocket()

        s.run()




#!/usr/bin/python

#-*-coding:utf-8 -*-

# 导入模块

import my9685

import time

if __name__ == '__main__':

    speed = 3000

    time1 = 1

    my9685.forward(speed,0)

    time.sleep(time1)

    print("小车以%d的脉冲前进了%d秒\n其坐标为(%d,%d)" % (speed, time1, 0, speed*time1))




import my9685

import avoid

import client

import time

import local


def run():

    while True:

        local.local()

        dis = avoid.distance()

        if dis < 30:

            client.start()

            time.sleep(0.3)

            my9685.back(3000,0)

            time.sleep(1)

            my9685.right(3000,0)

            time.sleep(1)

        else:

            client.start()

            time.sleep(0.3)

            my9685.forward()

if __name__ == '__main__':

    run()




#!/usr/bin/python

#coding: utf8

from __future__ import division                             #导入 __future__ 文件的 division 功能函数(模块、变量名....)   #新的板库函数   //=  

import time

import avoid

import my9685

import Adafruit_PCA9685                                     #导入Adafruit_PCA9685模块  


pwm = Adafruit_PCA9685.PCA9685()                            #把Adafruit_PCA9685.PCA9685()引用地址赋给PWM标签  

 

def set_servo_angle(channel, angle):              #输入角度转换成12^精度的数值  

    date=4096*((angle*11)+500)/20000              #进行四舍五入运算 date=int(4096*((angle*11)+500)/(20000)+0.5)     

    pwm.set_pwm(channel, 0, int(date))  

pwm.set_pwm_freq(300)  

 

print('Moving servo on channel x, press Ctrl-C to quit...')  


def main():

    pwm.set_pwm(12,0,1300)

    time.sleep(2)

    pwm.set_pwm(12,0,0)

    time.sleep(1)

   

    avoid.setup()

    dis = avoid.distance()

    print(dis)

    if dis < 80:

        print("小于80...")

        while 1:

            dis = avoid.distance()

            print(dis)

            if dis < 10:

                break

            else:

                my9685.forward(1000,0)

        my9685.stop()

           

        my9685.forward(1000,0)

        time.sleep(0.2)

        my9685.stop()

        time.sleep(1)

       

        pwm.set_pwm(13,0,500)

        time.sleep(0.5)

       

        my9685.back(1000,0)

        time.sleep(2)

        my9685.stop()

        pwm.set_pwm(13,0,0)

    else:

        print("大于80...")

        while 1:

            dis = avoid.distance()

            print(dis)

            if dis < 80:

                break

            else:

                my9685.forward(1000,0)

       

        dis = avoid.distance()

        if dis < 80:

            print("小于80...")

            while 1:

                dis = avoid.distance()

                print(dis)

                if dis < 10:

                    break

                else:

                    my9685.forward(1000,0)

            my9685.stop()

               

            my9685.forward(1000,0)

            time.sleep(0.2)

            my9685.stop()

            time.sleep(1)

           

            pwm.set_pwm(13,0,500)

            time.sleep(0.5)

           

            my9685.back(1000,0)

            time.sleep(2)

            my9685.stop()

            pwm.set_pwm(13,0,0)

if __name__ == '__main__':

    try:

        main()

    except KeyboardInterrupt:

        avoid.destroy()

        my9685.stop()

        print("finshed!")




#!/usr/bin/python

#-*-coding:utf-8 -*-

# 导入模块

import socket

import cv2

import numpy as np

# 定义IP与端口,此IP为服务端,即PC端

host_port = ('192.168.43.119', 8080)

# 定义图像长宽大小

capture_frame_width = 1280

capture_frame_height = 960


# 定义客户端套接字类

class ClientSocket(object):

    # 定义初始化函数

    def __init__(self):


        # socket.AF_INET用于服务器与服务器之间的网络通信

        # socket.SOCK_STREA M代表基于TCP的流式socket通信

        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self.client_socket.connect(host_port)                                   # 链接服务器

        self.capture = cv2.VideoCapture(0)                                      # 配置选择摄像头,只有一个摄像头,即是0

        self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, capture_frame_width)         # 设置图像的宽度

        self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, capture_frame_height)       # 设置图像的高度

        self.encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 45]                 # 设置编码参数

    # 定义图像采集函数

    def run(self):                                                              # 定义函数运行图片传输

        # 从摄像头获取图片

        ret, frame = self.capture.read()                                        # ret返回图片的读到与否,frame表示一幅图片。

        if ret:                                                                 # 判断是否有图片传输

            # 首先对图片进行编码,因为socket不支持直接发送图片

            result, imgencode = cv2.imencode('.jpg', frame, self.encode_param)

            data = np.array(imgencode)                                          # 将图片转化为一个矩阵,并赋给data

            stringData = data.tostring()                                        # 转化为字符串

            # print(stringData)

            self.client_socket.send(bytes(stringData))                          # 将图片编码信息发送给PC

            data_r = self.client_socket.recv(50)                                # 接收server发送的返回信息,最多50个字节

            # print(data_r)

            self.client_socket.close()                                          # 传送完成,关闭套接字

            cv2.destroyAllWindows()                                             # 销毁摄像头窗口


# 开始采集图像

def start():

    try:

        s = ClientSocket()                                                          # 类的实例化

        s.run()                                                                     # 运行类里面的run函数

    except cv2.error:

        pass





控制系统设计

1. 全地形月球车控制系统的设计

1.1 引言

      月球车的总体设计不能够少了控制系统的参与,机械就好比一个没有灵魂的冷冰冰的躯壳,如果去除了电力系统与中央控制器的驱动,其就没有了生命力。月球车控制系统包括行走系统即电机的驱动控制、定位系统的控制、摄像头采集图像控制、超声波距离传感器控制、机械手样件采集控制等。本重点介绍全地形月球车控制系统的设计,并针对其要求做出整体性规划,包括传感器的选择与硬件连接等。

1.2 全地形月球车控制系统原理与硬件设计

      月球车通过主控制芯片控制其余各传感器,使其完成预定的目标。具体实现为各个传感器将收集到的信号经过程序处理过后传回主控制器,主控制器根据编写的程序来执行各个传感器所应该要执行的动作。如下图所示:

主控制器与各个模块控制关系

      USB摄像头采集图像,一旦开启摄像头,那么摄像头以频率为每秒一张的速度采集图像,并将其发送给主控制器,主控制器通过处理决定是否继续采集图像;类似的,超声波传感器将采集到的距离信息发送给主控制器,主控制器根据距离的远近来控制月球车的前进后退左转右转,如果超声波传回来的距离数据小于20cm,那么此时主控制器调用摄像头模块采集图像,然后后退,反之则继续前进。如下图所示:

主控制器控制电机、摄像头与USB摄像头传感器

      相同的,主控制器控制电机前进,如果超声波距离传感器的值小于80cm,则代表碰到了障碍物,可通过图像查看是否为样件,如果是样件,那么调用机械手采集程序采集样件,如果不是,那么后退,继续寻找。如下图所示:

主控制器控制机械手采集

      关于定位系统,本作品采用不是特别精确的定位系统,如月球车以100cm/s的速度向前行进了1s,则其向前移动的距离为0.1m,在一个笛卡尔坐标系(直角坐标系)中就可以明确的标定出其所在的位置,前提是以月球车落地点为参考建立坐标系。如下图所示:

定位系统控制

1.3 主要器件选型

1.3.1 Basra主控板和树莓派主控板

      月球车模型选择的主控制板为Basra主控板,Basra是一款基于Arduino开源方案设计的一款开发板.板子上的微控制器可以在Arduino、eclipse、VisualStudio等IDE中通过c/c++语言来编写程序,编译成二进制文件,烧录进微控制器。Basra的处理器核心是ATmega328,同时具有14路数字输入/输出口(其中6路可作为PWM输出),6路模拟输入,一个16MHz晶体振荡器,一个USB口,一个电源插座,一个ICSPheader和一个复位按钮。

      Basra可作为机器人的控制核心,用于控制舵机、直流电机、传感器、输出模块、通信模块等。上位机主控制板为Raspberry Pi 3B+(树莓派),它的别名又叫卡片机,最小系统,这是因为其尺寸仅为82mmx 56mmx 19.5mm,重量仅为50克。采用树莓派作为控制系统的优势明显,它是基于一整个系统,这也就是说本文可以在类似于linux或者windows上编写程序,是编写程序界面更加的简化与便捷。

1.3.2 BigFish扩展板GPIO扩展板

      探索者设备中常见的扩展板有Bigfish综合扩展板、Birdmen手柄扩展板、Seahorse电机扩展板等。通过BigFish扩展板连接的电路可靠稳定,上面还扩展了伺服电机接口、直流电机驱动以及一个通用扩展接口,可以说的arduino控制板的必备配件。采用本扩展板的原因是其价格便宜,功能强大,且易于操作。

      使用树莓派的过程中,GPIO扩展板是必不可少的。顾名思义,GPIO扩展板即是通用型输入输出,它的作用是将树莓派中的一个GPIO接口扩展为多个输出,且其输出信号是完全相同,这就意味着本文可以同时控制一个乃至多个具有相同信号输入的传感器,比如本次需要用到的传感器如舵机、电机等。本次采用的GPIO扩展板是 40P灰色连接线。

1.3.3 舵机

      “探索者”器材中常见的伺服电机,是常用于航模舵位控制的小型伺服电机,一般也被称作“舵机”,分为标准舵机与圆周舵机两种,其重要参数见下表。

1 舵机参数


速度

扭力

转动角度

额定电压

Sec/60º

kg·cm

oz·in

V

标准舵机

0.13

2.9

40.3

±90º

6

圆周舵机

78rpm

2.2

30.61

360°

6



1.3.4 直流减速电机

      本实验采用的是DC3V-6V直流减速电机,又称直调双轴减速马达,它具有强磁抗干扰的优点,其减速比达到了1:48,工作电压为3-6V。其详细参数见表。

2 电机参数

名称

减速比

优点

额定电压

空载电流

空载转速

直调双轴减速马达

1:48

强磁、抗干扰

6V

200mA

200±10%rmp



型号

供电方式

静态电流

高电平输出

低电平输出

感应角度

探测距离

HC-SR04

DC5V

小于20mA

5V

0V

小于15度

2-450cm



1.3.5 脉冲脉宽调制(PWM)发生器

      不管是电机还是舵机,都是采用脉冲脉宽调制(以下简称PWM),即常说的占空比,控制线(信号输入线)输入一个可调制的周期性方波脉冲信号,方波脉冲信号的周期为20ms(频率为50Hz)。它的工作情况如下,PWM发生器会发送脉冲给需要的传感器,当然,脉冲值肯定是在其范围之内,例如范围为0-4096。如果脉冲数越多,即是方波的脉冲宽度变大,则代表其所控制的传感器占据高电平的时间越多,转角变大。反之,则转角变小。一般舵机的电压为4~6V,通常取值为5V。现举例一种常用的舵机角度变化与PWM信号的关系。舵机转角为0-180°,现将其分为250份,那么显而易见的,一份的度数为0.72°,其脉冲宽度为0.5ms-2.5ms,宽度为2ms,周期为20ms。因为2ms/250=8us,则pwm=1°/8us。转化为数学表达式如下:

那么角度=0.72*N,PWM=0.5N*0.8;具体见下表。

3 舵机角度与PWM关系

角度

N

PWM

0

0

0.5ms

45

3E

1ms

90

7D

1.5ms

135

BB

2ms

角度

N

PWM

180

FA

2.5ms




舵机角度与PWM关系

1.3.6 USB摄像头

      月球车如果想要采集图像,那么摄像头是必不可少的传感器,本作品采用USB传感器,此摄像头可直接插入USB插口使用,避免了采用软排线的连接,价格便宜、安全且简单可靠。在本文中,摄像头起到了采集图像与视频的作用,所以清晰度不需要太高,这是因为高像素的摄像头会导致图像特别大,降低传输的实时性,造成较高的延时率。本实验摄像头总体大小为长40mm,宽30mm,高25mm;其大帧频为30,镜头为透光玻璃,对焦方式为手动对焦,其带有可伸缩线,可以随时变换位置而不用担心线的长度不够等问题。具体如下图所示:

摄像头及其参数

1.3.7 超声波测距模块

      超声波测距模块采用通用性较强的HC-SR04,它采用DC5V作为供电方式,静态电流小于2mA,电平输出分别为0V和5V,感应角度小于15度,探测距离能达到2-450cm,能够较好的探测障碍物,让主控制器做出反应。实物如下图所示,其具体参数见下表。

4 超声波传感器参数对照表

超声波传感器实物图

1.4硬件系统连接展示图

硬件系统连接图

2. 控制语言编程实现

2.1 引言

      有了机械结构的整体支持,还有整个月球车的控制流程,但是目前还差的即是整个月球车的灵魂:程序。编写程序是一个很重要的环节,一个很重要使用的算法能够大量的节省人力物力,一门好的编程语言的选择能够尽可能的减少代码工作量,以减少各种消耗。本将会从经济实用的角度选择出整个系统的控制语言,并贴写出主要的控制代码,以妄能够更加清楚的解释月球车的整个控制结构。

2.2 控制系统实现代码

2.2.1 月球车避障与结果分析

      在之前写到了总体控制系统的结构图,这意味着本文可以直接根据程序框图来编程程序,这大大减少了工作量与工作时间。但是还是需要编写各程序控制框图,如下图所示:

避障程序框图

程序代码如下

本段代码定义了如下几个函数:

      ① 端口安装函数

      ② 测距函数

      ③ 循环测距函数

      ④ 端口销毁函数

      将上述代码写入控制器,使控制器控制超声波传感器工作,可以明确的观察到当月球车在行进的过程中不断的有数据产生,这个数据即是月球车距离传感器所观察到的距离,达到了本项目所要求的期望。本文后面将会针对此距离做进一步的分析与调整,详细做法为:当月球车检测到某一距离值时,做出相应的反应,这个反应可以是后退,左右转,亦或者是利用机械手采集样件。当然上述所提到的距离值可以是20cm,30cm,或者是另外规定的某一个合理的范围值。其避障效果如下图所示:

避障效果图

2.2.2 月球车越障与结果分析

      越障功能没有特定的代码,即是以某一速度前进或者后退,那么就是控制电机。其程序框图如下图所示:

越障移动程序框图

代码如下

在本段代码中,定义了几个函数,分别为:

      前进函数,其作用即是使月球车根据给定的速度前进

      后退函数,其作用即是使月球车根据给定的速度后退

      左转函数,其作用即是使月球车根据给定的速度左转

      右转函数,其作用即是使月球车根据给定的速度右转

      停止函数,此函数中,不需要给电机传输任何PWM波,那么电机就会停止旋转

      端口销毁,完成任务之后调用此函数,将会解除对于GPIO口的占用,让其他的函数也能调用此端口

      主函数

      测试结果表明,月球车能够很好的根据此代码工作,达到了预期目标。但是其越障性能不强,其主要原因是六轮半径较小,较为容易卡底盘,以后的改进中希望能够采用更大的轮胎进行测试。如下图所示:

越障测试

2.2.3 月球车图像传输与结果分析

      图像传输方式需要用到网络编程,即是让月球车模型与PC控制端处于同一局域网内,这样他们才能进行相互之间的通讯,然后本项目需要建立他们彼此之间的通道,那么应该有两部分代码,一部分为树莓派控制端,即月球车端的代码,这里将月球端作为客户端;另一部分为地球控制端,即PC端作为服务器,这样月球端的图像能源源不断的通过局域网传到PC端。

      但是,服务端与客户端之间建立连接有多种方式,常见的有UDP(User Datagram Protocol)和TCP(Transmission Control Protocol),即用户数据包协议和传输控制协议。二者之间的区别通俗的讲是TCP较UDP稳定,TCP互传数据一般不会出现丢包的现象,这是因为当服务器收到客户端的信息过后将会回信息给客户端,如果传输失败,则会进行二次传输;而当UDP收到客户端所传输的信息过后不会反馈信息,这就导致了丢包现象的出现。下面简单的讲解TCP客户端和服务器之间的关系。TCP客户端如下图所示:

客户端图像传输框图

客户端代码如下

服务端(PC端)如下图所示:

服务端(PC端)程序框图

服务端代码如下

      此程序分为两部分,服务端与客户端。经过许久的测试得知,月球车和地球之间能够建立良好的关系,并在此关系上进行了稳定的数据传输工作。达到了预定的目标,遗憾的是,本段程序仅仅只能够进行图片的传输,而不能进行视频数据流之间的流畅传输,期待改进为能够进行视频数据流之间的传输程序。如下图所示:

图像传输测试

2.2.4 定位系统流程图与代码

      月球车定位在前面一章已经介绍,主要是通过坐标系建立,如下图所示:

定位坐标系

定位程序框图

代码如下

      根据测试表明,测定位方式并不是十分精确,只能够大概的表示出月球车的方位,基本达到了预期,但是就月球车本身而言还远远未达到。改进方案即是根据天线进行定位,必须用得到数据流之间的传输。如下图所示:

定位测试

2.2.5 机械手样件采集与结果分析

      机械手样件采集其作用为利用月球车前方的机械手进行样件采集。在月球探索过程中,肯定需要采集月球样本并将其带回地球做进一步的分析,这为人类对于月球的理解以及今后对于月球的探索奠定了一定的基石。本文模拟了月球车采样的过程,其利用机械手与超声波进行采集。具体代码流程如下图所示:

机械手采集系统框图

具体代码如下

      本程序主要定义了避障与采集两种函数,避障函数主要用来探测距离障碍物的距离,而采集函数则根据合适位置进行样件采集。经过测试得知,其基本满足了以上的样件采集功能,见图。只是部分功能还有待优化,如将超声波传感器换成其他摄像头传感器。如摄像头传感器识别到样件那么就会进行采集,这样会更加的精确与适当。

2.2.6 代码总成与结果分析

      将上述所有功能集合到一个总的程序中,其基本实现思路如下图所示:

程序总成框图

其代码如下:

      根据代码运行结果分析可知,此代码可根据所编写执行,也在一定程度上达到了所预期。但还有提升的希望,即是前面提到的将超声传感器换为摄像机处理,希望在以后的研究中能有所改善。

* 本项目未获得作者开源授权,无法提供资料下载

© 2024 机器时代(北京)科技有限公司  版权所有
机器谱——机器人共享方案网站
学习  开发  设计  应用