HOKUYO激光雷达的python驱动+串口读取旋转编码器的数据读取初级调试1

综述

本文总共有三部分,第一部分是URG-04的python驱动调试,第二部分是python串口读取旋转编码器数据.第三部分是将URG-04数据和串口数据合并调试。

第1部分:URG-04的python驱动调试

1.1 serial_port.py编写

# -*- coding: utf-8 -*-
# E:\Anaconda3\python.exe			 #可以根据实际情况改或删除

import struct
import sys


class SerialPort(object):
    def __init__(self, serial_port):
        self.__port = serial_port
        self.__checksum = 0

    def close(self):
    	 self.__port.close()

    def get_checksum(self):
        return self.__checksum

    def read(self, size):
        char = self.__port.read(size)
        if sys.version_info >= (3,0,0):
            char = str(char, 'UTF-8')
        return char

    def write(self, char):
        if sys.version_info >= (3,0,0):
        	char = bytes(char, 'UTF-8')
        self.__port.write(char)

    def send_command(self, address, command):
        self.__checksum = address
        self.__port.write(chr(address))
        self.__checksum += command
        self.__port.write(chr(command))

    def read_byte(self):
        res = self.__port.read(1)
        if len(res) > 0:
            val = struct.unpack('>B', res)
            self.__checksum += val[0] & 0xFF
            return val[0]
        return None

    def read_sbyte(self):
        res = self.__port.read(1)
        if len(res) > 0:
            val = struct.unpack('>b', res)
            self.__checksum += val[0] & 0xFF
            return val[0]
        return None

    def read_word(self):
        res = self.__port.read(2)
        if len(res) > 0:
            val = struct.unpack('>H', res)
            self.__checksum += val[0] & 0xFF
            self.__checksum += (val[0] >> 8) & 0xFF
            return val[0]
        return None

    def read_sword(self):
        res = self.__port.read(2)
        if len(res) > 0:
            val = struct.unpack('>h', res)
            self.__checksum += val[0] & 0xFF
            self.__checksum += (val[0] >> 8) & 0xFF
            return val[0]
        return None

    def read_long(self):
        res = self.__port.read(4)
        if len(res) > 0:
            val = struct.unpack('>L', res)
            self.__checksum += val[0] & 0xFF
            self.__checksum += (val[0] >> 8) & 0xFF
            self.__checksum += (val[0] >> 16) & 0xFF
            self.__checksum += (val[0] >> 24) & 0xFF
            return val[0]
        return None

    def read_slong(self):
        res = self.__port.read(4)
        if len(res) > 0:
            val = struct.unpack('>l', res)
            self.__checksum += val[0] & 0xFF
            self.__checksum += (val[0] >> 8) & 0xFF
            self.__checksum += (val[0] >> 16) & 0xFF
            self.__checksum += (val[0] >> 24) & 0xFF
            return val[0]
        return None

    def write_byte(self, val):
        self.__checksum += val & 0xFF
        return self.__port.write(struct.pack('>B', val))

    def write_sbyte(self, val):
        self.__checksum += val & 0xFF
        return self.__port.write(struct.pack('>b', val))

    def write_word(self, val):
        self.__checksum += val & 0xFF
        self.__checksum += (val >> 8) & 0xFF
        return self.__port.write(struct.pack('>H', val))

    def write_sword(self, val):
        self.__checksum += val & 0xFF
        self.__checksum += (val >> 8) & 0xFF
        return self.__port.write(struct.pack('>h', val))

    def write_long(self, val):
        self.__checksum += val & 0xFF
        self.__checksum += (val >> 8) & 0xFF
        self.__checksum += (val >> 16) & 0xFF
        self.__checksum += (val >> 24) & 0xFF
        return self.__port.write(struct.pack('>L', val))

    def write_slong(self, val):
        self.__checksum += val & 0xFF
        self.__checksum += (val >> 8) & 0xFF
        self.__checksum += (val >> 16) & 0xFF
        self.__checksum += (val >> 24) & 0xFF
        return self.__port.write(struct.pack('>l', val))

1.2kokuyo.py编写

# -*- coding: utf-8 -*-
import threading
import traceback
import sys
import time


def chunks(l, n):
    for i in range(0, len(l), n):
        yield l[i:i + n]


def decode(val):
    bin_str = '0b'
    for char in val:
        val = ord(char) - 0x30
        bin_str += '%06d' % int(bin(val)[2:])
    return int(bin_str, 2)


class Hokuyo(object):
    SHORT_COMMAND_LEN = 5
    MD_COMMAND_REPLY_LEN = 20

    LASER_ON = 'BM\n'
    LASER_OFF = 'QT\n'
    RESET = 'RS\n'

    VERSION_INFO = 'VV\n'
    SENSOR_STATE = 'II\n'
    SENSOR_SPECS = 'PP\n'
    SET_SCIP2 = 'SCIP2.0\n'

    CHARS_PER_VALUE = 3.0
    CHARS_PER_LINE = 66.0
    CHARS_PER_BLOCK = 64.0

    START_DEG = 119.885
    STEP_DEG = 0.35208516886930985

    START_STEP = 44
    STOP_STEP = 725

    VERSION_INFO_LINES = 6
    SENSOR_STATE_LINES = 8
    SENSOR_SPECS_LINES = 9

    def __init__(self, port):
        self.__port = port
        self.__port_lock = threading.RLock()

        self.__timestamp, self.__angles, self.__distances = 0, [], []
        self.__scan_lock = threading.Lock()

        self.__is_active = True
        self.__scanning_allowed = False

    def __offset(self):
        count = 2
        result = ''

        self.__port_lock.acquire()
        try:
            a = self.__port.read(1)
            b = self.__port.read(1)

            while not ((a == '\n' and b == '\n') or (a == '' and b == '')):
                result += a
                a = b
                b = self.__port.read(1)
                count += 1
        finally:
            self.__port_lock.release()

        result += a
        result += b

        sys.stderr.write('READ %d EXTRA BYTES: "%s"\n' % (count, str(result)))

    def __execute_command(self, command):
        self.__port_lock.acquire()
        try:
            self.__port.write(command)
            result = self.__port.read(len(command))
            assert result == command
        finally:
            self.__port_lock.release()
        return result

    def __short_command(self, command, check_response=True):
        result = ''
        self.__port_lock.acquire()
        try:
            # noinspection PyBroadException
            try:
                result += self.__execute_command(command)
                result += self.__port.read(Hokuyo.SHORT_COMMAND_LEN)

                if check_response:
                    assert result[-5:-2] == '00P'
                assert result[-2:] == '\n\n'

                return result
            except BaseException as e:
                sys.stderr.write('RESULT: "%s"' % result)
                traceback.print_exc()
                self.__offset()
        finally:
            self.__port_lock.release()

    def __long_command(self, cmd, lines, check_response=True):
        result = ''
        self.__port_lock.acquire()
        try:
            # noinspection PyBroadException
            try:
                result += self.__execute_command(cmd)

                result += self.__port.read(4)
                if check_response:
                    assert result[-4:-1] == '00P'
                assert result[-1:] == '\n'

                line = 0
                while line < lines:
                    char = self.__port.read_byte()
                    if not char is None:
                        char = chr(char)
                        result += char
                        if char == '\n':
                            line += 1
                    else:  # char is None
                        line += 1

                assert result[-2:] == '\n\n'

                return result
            except BaseException as e:
                sys.stderr.write('RESULT: "%s"' % result)
                traceback.print_exc()
                self.__offset()
        finally:
            self.__port_lock.release()

    def terminate(self):
        self.reset()

        self.__is_active = False
        self.__port_lock.acquire()
        try:
            self.__port.close()
        finally:
            self.__port_lock.release()

    def laser_on(self):
        return self.__short_command(Hokuyo.LASER_ON, check_response=True)

    def laser_off(self):
        return self.__short_command(Hokuyo.LASER_OFF)

    def reset(self):
        return self.__short_command(Hokuyo.RESET)

    def set_scip2(self):
        # "for URG-04LX"
        return self.__short_command(Hokuyo.SET_SCIP2, check_response=False)

    def set_motor_speed(self, motor_speed=99):
        return self.__short_command('CR' + '%02d' % motor_speed + '\n', check_response=False)

    def set_high_sensitive(self, enable=True):
        return self.__short_command('HS' + ('1\n' if enable else '0\n'), check_response=False)

    def get_version_info(self):
        return self.__long_command(Hokuyo.VERSION_INFO, Hokuyo.VERSION_INFO_LINES)

    def get_sensor_state(self):
        return self.__long_command(Hokuyo.SENSOR_STATE, Hokuyo.SENSOR_STATE_LINES)

    def get_sensor_specs(self):
        return self.__long_command(Hokuyo.SENSOR_SPECS, Hokuyo.SENSOR_SPECS_LINES)

    def __get_and_parse_scan(self, cluster_count, start_step, stop_step):
        distances = {}
        result = ''

        count = ((stop_step - start_step) * Hokuyo.CHARS_PER_VALUE * Hokuyo.CHARS_PER_LINE)
        count /= (Hokuyo.CHARS_PER_BLOCK * cluster_count)
        count += 1.0 + 4.0  # paoolo(FIXME): why +4.0?
        count = int(count)

        self.__port_lock.acquire()
        try:
            result += self.__port.read(count)
        finally:
            self.__port_lock.release()

        assert result[-2:] == '\n\n'

        result = result.split('\n')
        result = [line[:-1] for line in result]
        result = ''.join(result)

        i = 0
        start = (-Hokuyo.START_DEG + Hokuyo.STEP_DEG * cluster_count * (start_step - Hokuyo.START_STEP))
        for chunk in chunks(result, 3):
            distances[- ((Hokuyo.STEP_DEG * cluster_count * i) + start)] = decode(chunk)
            i += 1

        return distances

    def get_single_scan(self, start_step=START_STEP, stop_step=STOP_STEP, cluster_count=1):
        self.__port_lock.acquire()
        # noinspection PyBroadException
        try:
            cmd = 'GD%04d%04d%02d\n' % (start_step, stop_step, cluster_count)
            self.__port.write(cmd)

            result = self.__port.read(len(cmd))
            assert result == cmd

            result += self.__port.read(4)
            assert result[-4:-1] == '00P'
            assert result[-1] == '\n'

            result = self.__port.read(6)
            assert result[-1] == '\n'

            scan = self.__get_and_parse_scan(cluster_count, start_step, stop_step)
            return scan

        except BaseException as e:
            traceback.print_exc()
            self.__offset()

        finally:
            self.__port_lock.release()

    def __get_multiple_scans(self, start_step=START_STEP, stop_step=STOP_STEP, cluster_count=1,
                             scan_interval=0, number_of_scans=0):
        self.__port_lock.acquire()
        # noinspection PyBroadException
        try:
            cmd = 'MD%04d%04d%02d%01d%02d\n' % (start_step, stop_step, cluster_count, scan_interval, number_of_scans)
            self.__port.write(cmd)

            result = self.__port.read(len(cmd))
            assert result == cmd

            result += self.__port.read(Hokuyo.SHORT_COMMAND_LEN)
            assert result[-2:] == '\n\n'

            index = 0
            while number_of_scans == 0 or index > 0:
                index -= 1

                result = self.__port.read(Hokuyo.MD_COMMAND_REPLY_LEN)
                assert result[:13] == cmd[:13]

                result = self.__port.read(6)
                assert result[-1] == '\n'

                scan = self.__get_and_parse_scan(cluster_count, start_step, stop_step)
                yield scan

        except BaseException as e:
            traceback.print_exc()
            self.__offset()

        finally:
            self.__port_lock.release()

    def enable_scanning(self, _enable_scanning):
        self.__scanning_allowed = _enable_scanning

    def __set_scan(self, scan):
        if scan is not None:
            timestamp = int(time.time() * 1000.0)
            angles, distances = Hokuyo.__parse_scan(scan)

            self.__scan_lock.acquire()
            try:
                self.__angles, self.__distances, self.__timestamp = angles, distances, timestamp
            finally:
                self.__scan_lock.release()

    def get_scan(self):
        if not self.__scanning_allowed:
            scan = self.get_single_scan()
            self.__set_scan(scan)

        self.__scan_lock.acquire()
        try:
            return self.__angles, self.__distances, self.__timestamp
        finally:
            self.__scan_lock.release()

    def scanning_loop(self):
        while self.__is_active:
            if self.__scanning_allowed:
                self.__port_lock.acquire()
                for scan in self.__get_multiple_scans():
                    self.__set_scan(scan)
                    if not self.__scanning_allowed or not self.__is_active:
                        self.laser_off()
                        self.laser_on()
                        self.__port_lock.release()
                        break
            time.sleep(0.1)

    @staticmethod
    def __parse_scan(scan):
        angles = sorted(scan.keys())
        distances = list(map(scan.get, angles))
        return angles, distances

1.3 test_drive_hokuyo.py

#!\Anaconda3\python.exe  #可以根据实际情况改或删除
# -*- coding: utf-8 -*-
import serial

import hokuyo
import serial_port

uart_port = 'COM14'  	#根据实际情况改端口
uart_speed = 115200

if __name__ == '__main__':
    laser_serial = serial.Serial(port=uart_port, baudrate=uart_speed, timeout=0.5)
    port = serial_port.SerialPort(laser_serial)

    laser = hokuyo.Hokuyo(port)
    print('laser_on(): ')
    print(laser.laser_on())
    print('---')
    print('laser.get_single_scan():')
    print(laser.get_single_scan())
    print('---')
    print('laser.get_version_info(): ')
    print(laser.get_version_info())
    print('---')
    print('laser.get_sensor_specs(): ')
    print(laser.get_sensor_specs())
    print('---')
    print('laser.get_sensor_state(): ')
    print(laser.get_sensor_state())
    print('---')
    print('laser.set_high_sensitive()')
    print(laser.set_high_sensitive())
    print('---')
    print('laser.set_high_sensitive(False)')
    print(laser.set_high_sensitive(False))
    print('---')
    print('laser.set_motor_speed(10)')
    print(laser.set_motor_speed(10))
    print('---')
    print('laser.set_motor_speed()')
    print(laser.set_motor_speed())
    print('---')
    print('laser.reset()')
    print(laser.reset())
    print('---')
    print('laser.laser_off()')
    print(laser.laser_off())

将三个文件放在一个文件夹中,测试结果:

在这里插入图片描述

第2部分:python串口读取旋转编码器数据

2.1 下位机Arduino读取旋转编码器的数据并发送

本节概述:首先使Arduino读取旋转编码器的数据,其次调通Arduino的串口通信,最后调试:将旋转编码器的数据通过串口传输到上位机上。

Arduino的程序代码:

/*
 * File Name: uart_test.c
 * Description: Encode and Serial Event example
 *              When new serial data arrives, this sketch adds it to a String.
 *               When a newline is received, the loop prints the string and clears it.
 * Author: yuyu
 * Create Date: 2019-06-17
 */

String inputString = "";         // a String to hold incoming data
bool stringComplete = false;  // whether the string is complete

//int val; 
int encoder0PinA = 6;   //Rotary encoder phase A pin
int encoder0PinB = 7;   //Rotary encider phase B pin
long int encoder0Pos = 0; //count the namber of encoder
int encoder0PinALast = LOW;
int n = LOW; 


 void setup() { 
  pinMode (encoder0PinA,INPUT);
  pinMode (encoder0PinB,INPUT);
   
  Serial.begin (115200);
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);
 } 

 void loop() { 
   n = digitalRead(encoder0PinA);
   if ((encoder0PinALast == LOW) && (n == HIGH)) {//上升沿
     if (digitalRead(encoder0PinB) == LOW) {
       encoder0Pos--;
     } else {
       encoder0Pos++;
     }
    Serial.println (encoder0Pos);
//     Serial.print ("mm\n");
   } 
   encoder0PinALast = n;

  // print the string when a newline arrives:
  if (stringComplete) {
    Serial.println(inputString);
    Serial.println (encoder0Pos);
    // clear the string:
    inputString = "";
    stringComplete = false; 
    } 
 }

/*
  SerialEvent occurs whenever a new data comes in the hardware serial RX. This
  routine is run between each time loop() runs, so using delay inside loop can
  delay response. Multiple bytes of data may be available.
*/
void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag so the main loop can
    // do something about it:
    if (inChar == '\n') {
      stringComplete = true;
    }
  }
} 

2.2 上位机的测试简易版python代码

serial_test.py:

# E:\Anaconda3\python.exe
# -*- coding: utf-8 -*-
import serial

serialPort = "COM7"  # 串口
baudRate = 115200  # 波特率
ser = serial.Serial(serialPort, baudRate, timeout=0.5)
print("参数设置:串口=%s ,波特率=%d" % (serialPort, baudRate))

while 1:
	str = ser.readline()
	print(str)

ser.close()

2.3 测试结果

在这里插入图片描述

第3部分:将URG-04数据和串口数据合并调试

3.1 liar_serial_encode.py的编写

import serial
import hokuyo
import serial_port

uart_port = 'COM14'		#激光雷达URG-40的读取数据的串口设置
uart_speed = 115200

serialPort = "COM7"  # 串口旋转编码器
baudRate = 115200

if __name__ == '__main__':
    laser_serial = serial.Serial(port=uart_port, baudrate=uart_speed, timeout=0.5)
    port = serial_port.SerialPort(laser_serial)
    ser = serial.Serial(serialPort, baudRate, timeout=0.5)

    while 1:
        laser = hokuyo.Hokuyo(port)
        print('laser_on(): ')
        print(laser.laser_on())
        print('laser.get_single_scan():')
        print(laser.get_single_scan())
        print(laser.get_single_scan())
        print('---')
        print('laser.set_motor_speed(10)')
        print(laser.set_motor_speed(10))
        print('---')
        print('laser.set_motor_speed()')
        print(laser.set_motor_speed())
        print('---')
        print('laser.reset()')
        print(laser.reset())
        print('---')
        str = ser.readline()
        print(str)
    ser.close()

3.1 测试结果

在这里插入图片描述

总结

本次写的博客就是:调试各个模块然后,然后简单地将旋转编码器数据加入激光雷达的数据中,后期还要将整个数据其进一步优化。

激光雷达采集的部分数据如下:

119.885: 0,
 119.5329148311307: 0,
 119.18082966226139: 0,
 118.82874449339208: 0,
 118.47665932452277: 0,
 118.12457415565346: 0,
 117.77248898678414: 0,
 117.42040381791483: 0,
 117.06831864904552: 0,
 116.71623348017621: 0,
 116.3641483113069: 0,
 116.0120631424376: 0,
 115.65997797356829: 7,
 115.30789280469898: 0,
 114.95580763582967: 0,
 114.60372246696036: 0,
 114.25163729809105: 0,
 113.89955212922173: 507,
 113.54746696035242: 480,
 113.19538179148311: 473,
 112.8432966226138: 473,
 112.4912114537445: 469,
 112.13912628487519: 445,
 111.78704111600588: 394,
 111.43495594713657: 347,
 111.08287077826726: 347,
 110.73078560939794: 347,
 110.37870044052863: 350,
 110.02661527165932: 7,
 109.67453010279002: 0,
 109.3224449339207: 0,
 108.9703597650514: 0,
 108.61827459618209: 0,
 108.26618942731278: 0,
 107.91410425844347: 0,
 107.56201908957416: 0,
 107.20993392070486: 0,
 106.85784875183555: 0,
 106.50576358296624: 0,
 106.15367841409692: 0,
 105.80159324522761: 0,
 105.4495080763583: 0,
 105.09742290748899: 0,
 104.74533773861968: 0,
 104.39325256975037: 0,
 104.04116740088106: 0,
 103.68908223201176: 0,
 103.33699706314243: 0,
 102.98491189427313: 0,
 102.63282672540382: 0,
 102.28074155653451: 0,
 101.9286563876652: 0,
 101.57657121879589: 0,
 101.22448604992658: 0,
 100.87240088105727: 0,
 100.52031571218797: 0,
 100.16823054331866: 0,
 99.81614537444935: 0,
 99.46406020558004: 0,
 99.11197503671073: 0,
 98.75988986784142: 0,
 98.4078046989721: 0,
 98.05571953010279: 0,
 97.70363436123348: 0,
 97.35154919236417: 0,
 96.99946402349487: 0,
 96.64737885462556: 0,
 96.29529368575625: 0,
 95.94320851688693: 0,
 95.59112334801762: 0,
 95.23903817914831: 0,
 94.886953010279: 0,
 94.53486784140969: 0,
 94.18278267254038: 0,
 93.83069750367108: 0,
 93.47861233480177: 1102,
 93.12652716593246: 1102,
 92.77444199706315: 7,
 92.42235682819384: 0,
 92.07027165932453: 0,
 91.71818649045522: 0,
 91.36610132158592: 0,
 91.0140161527166: 1278,
 90.66193098384728: 1278,
 90.30984581497798: 1278,
 89.95776064610867: 1278,
 89.60567547723936: 1278,
 89.25359030837005: 1227,
 88.90150513950074: 1171,
 88.54941997063143: 1171,
 88.19733480176211: 1171,
 87.8452496328928: 1171,
 ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值