SkoltechRobotics / rplidar

Python module for RPLidar A1 and A2 rangefinder scanners
MIT License
187 stars 126 forks source link

Update DTR commands #5

Closed ysshah closed 7 years ago

ysshah commented 7 years ago

I am using Python 3 with the RPLIDAR A2. I don't know too much about Serial port communication, but often when I restarted the code or ran it multiple times, I would get an Incorrect descriptor starting bytes error:

/Users/yash/Desktop/SmartWheels/test.py in run()
     22 def run():
     23     lidar = RPLidar('/dev/tty.SLAB_USBtoUART')
---> 24     info = lidar.get_info()
     25     health = lidar.get_health()
     26 

/Users/yash/anaconda3/lib/python3.5/site-packages/rplidar.py in get_info(self)
    209         '''
    210         self._send_cmd(GET_INFO_BYTE)
--> 211         dsize, is_single, dtype = self._read_descriptor()
    212         if dsize != INFO_LEN:
    213             raise RPLidarException('Wrong get_info reply length')

/Users/yash/anaconda3/lib/python3.5/site-packages/rplidar.py in _read_descriptor(self)
    187             raise RPLidarException('Descriptor length mismatch')
    188         elif not descriptor.startswith(SYNC_BYTE + SYNC_BYTE2):
--> 189             raise RPLidarException('Incorrect descriptor starting bytes')
    190         is_single = _b2i(descriptor[-2]) == 0
    191         return _b2i(descriptor[2]), is_single, _b2i(descriptor[-1])

RPLidarException: Incorrect descriptor starting bytes

or a Wrong body size error:

/Users/yash/Desktop/SmartWheels/test.py in update_line(num, iterator, line)
     10 
     11 def update_line(num, iterator, line):
---> 12     scan = np.array(next(iterator)).T
     13 
     14     angle = np.deg2rad((scan[1] + 90) % 360)

/Users/yash/anaconda3/lib/python3.5/site-packages/rplidar.py in iter_scans(self, max_buf_meas, min_len)
    355         scan = []
    356         iterator = self.iter_measurments(max_buf_meas)
--> 357         for new_scan, quality, angle, distance in iterator:
    358             if new_scan:
    359                 if len(scan) > min_len:

/Users/yash/anaconda3/lib/python3.5/site-packages/rplidar.py in iter_measurments(self, max_buf_meas)
    321             raise RPLidarException('Wrong response data type')
    322         while True:
--> 323             raw = self._read_response(dsize)
    324             self.logger.debug('Recieved scan response: %s' % raw)
    325             if max_buf_meas:

/Users/yash/anaconda3/lib/python3.5/site-packages/rplidar.py in _read_response(self, dsize)
    197         self.logger.debug('Recieved data: %s', data)
    198         if len(data) != dsize:
--> 199             raise RPLidarException('Wrong body size')
    200         return data
    201 

RPLidarException: Wrong body size

After some investigation, I visited the pySerial documentation and found that the setDTR method is deprecated. I think we need to use the dtr method instead. Then I noticed that in the init function, the flag dsrdtr must be set to True to "Enable hardware (DSR/DTR) flow control." With these edits to the code, my RPLIDAR A2 is able to work consistently. However, I still must call get_info() and/or get_health() before calling iter_scans() for some reason - not sure if this is unintended or by design. Please review these edits and let me know what you think. Thank you!

ysshah commented 7 years ago

Actually, I just realized that the reason for my bugs might be that my computer is trying to execute lidar methods before the lidar is fully initialized. Calling time.sleep(0.1) after initializing the lidar with lidar = RPLidar('/dev/ttyUSB0') seems to have solved the problem. Nevertheless, the changes in this pull request might be required to support future versions of pySerial, where the setDTR method might be completely removed.

newpavlov commented 7 years ago

Thank you for your contribution!

How about trying to use laser.stop() and laser.clear_input()? First command will stop measuring process, so sensor will stop sending data and the second one will clear the the serial port buffer. It seems what you have leftover data from previous run, current code is not able to handle such situations correctly. I'll think of how better to handle it, probably I will add checks if buffer empty or not.

I will try to check your changes in the following days.

jjehl commented 7 years ago

apparently dtr is only for a1, but I think you should not send the command for a1 and a2. Better to add a self.model ='a2' and filter the start and stop command on the model

newpavlov commented 7 years ago

@jjehl Do you experience any problems with DTR on A2? Serial protocol wise it should not create any problems. I would like to not introduce any distinctions between A1 and A2 without a real need for it.

newpavlov commented 7 years ago

@ysshah Sorry it longer than I intended. I've merged your PR, will publish it on PyPI in the following week.

jjehl commented 7 years ago

I didn't understand correctly your code. There isn't any problem with the DTR on my A2.

jtreg commented 5 years ago

any idea why I get ths error please?

It works for a while but the gives error


Traceback (most recent call last): File "python_osc_send.py", line 70, in client.send_message("/st", [ an, di]) File "/usr/local/lib/python3.5/dist-packages/pythonosc/udp_client.py", line 43, in send_message self.send(msg) File "/usr/local/lib/python3.5/dist-packages/pythonosc/udp_client.py", line 27, in send self._sock.sendto(content.dgram, (self._address, self._port)) BlockingIOError: [Errno 11] Resource temporarily unavailable


Sorry if its a bit untidy! I realise its to do with BlockingIOError and I have tried to enlighten myself from searching for a clue to solve it. Have you a fix/come across this issue?

Best,

James


The code is here:

"""Small example OSC client Extended to work with Lidar As part of work for final project MA Computational Art 2018 Version 0.0 james@tregaskis.org code adapted from https://pypi.org/project/python-osc/ and https://github.com/SkoltechRobotics/rplidar

This program sends values from Lidar sensorfrom www.coolcomponents.com /filter address, waiting for 1 seconds between each value. """ import argparse import random import time import sys from rplidar import RPLidar

from pythonosc import osc_message_builder from pythonosc import udp_client PORT_NAME = '/dev/ttyUSB0' lidar = RPLidar(PORT_NAME) lidar = RPLidar('/dev/ttyUSB0')

info = lidar.get_info() print(info)

health = lidar.get_health() print(health)

try: if name == "main": parser = argparse.ArgumentParser() parser.add_argument("--ip", default="192.168.0.40", help="The ip of the OSC server") parser.add_argument("--port", type=int, default=5005, help="The port the OSC server is listening on") args = parser.parse_args()

  client = udp_client.SimpleUDPClient(args.ip, args.port)
maxAngle=0

# angle_old=[None]*360;
# angle_new=[None]*360;
distance_old=[None]*360;
distance_new=[None]*360;

for measurment in lidar.iter_measurments():
    # strength of laser, angle of reading, distance

    # print(measurment[1],measurment[2],measurment[3])
    if int(measurment[1]) > 0:
        # print (int(measurment[1]))
        # print (int(measurment[2]))
        # angle_new[int(measurment[2])]=int(measurment[2])
        distance_new[int(measurment[2])]=int(measurment[3])
        # if angle_new[int(measurment[2])] != angle_old[int(measurment[2])] and
        if distance_new[int(measurment[2])] != distance_old[int(measurment[2])]:
        # if angle_new[30]!=angle_old[30] or distance_new[30]!=angle_old[30]:
            # st = (angle_new[int(measurment[2])])
            an = int(measurment[2])
            di = (distance_new[int(measurment[2])])
            print(an,di)
            # angle_old[int(measurment[2])]=angle_new[int(measurment[2])]

distance_old[int(measurment[2])]=distance_new[int(measurment[2])] client.send_message("/st", [ an, di])

client.send_message("/an", an)

            # client.send_message("/di", di)
            # print([st,an,di])
        #client.send_message("/angle", angles)
        #client.send_message("/strength", strengths)
        #client.send_message("/distance", distances)

       # print (angles)

        #client.send_message("/distance", int(measurment[3]))
    # else:
    #     if measurment[2]==None or measurment[2]=="":
    #         pass
    #     #strengths.append(int(measurment[1]))
    #     angles.append(int(measurment[2]))
    #     #distances.append(int(measurment[3]))
    #     maxAngle =  maxAngle+1
time.sleep(1)

except KeyboardInterrupt: print('Stopping.')

osc_terminate()

lidar.stop() lidar.disconnect()