ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
0

intermittent errors with pyserial based service - how to make it robust?

asked 2021-08-07 17:45:10 -0500

masynthetic gravatar image

updated 2021-08-07 23:03:20 -0500

I am working on making a serial communication ROS2 service based on pyserial in order to communicate with my microcontrollers (arduinos which have been left behind by micro-ROS) I would have loved a well supported solution to this but I have not been able to find one so this has been my best attempt. It has generally worked but I am running into an extremely frustrating issue when many requests are put on the service where the serial connection will essentially break for seemingly no reason, returning either of the following (depending on read or write):

    write failed: [Errno 5] Input/output error

or

    device reports readiness to read but returned no data (device disconnected?)

or occasionally the readlines will timeout and appears device is unresponsive

sometimes the device comes up at a new address which is why I have based my service on serial number rather than address, and I have basically programmed in some band aids which attempt to reconnect but while it does seem to reconnect successfully, the connection does not seem to be functional and times out.

I'm seriously stumped, all I really know is that it tends to happen when I am making calls to the service at very high frequency. I have tried increasing the timeouts to over 1 second, all calls to this service use a client.wait_for_service() call before ever sending the request so I don't think there is any reason calls should be overlapping.

well without further ado, here is my serial service. I am happy to answer any questions about it, I am also open to entirely new solutions to communicating with 8-bit microcontrollers

#!/usr/bin/env python3
import rclpy
import serial
import serial.tools.list_ports
import time

from serial import SerialException
from rclpy.node import Node 
from garden_interfaces.srv import SerialMessage


class SendRecieveSerialServerNode(Node):
    def __init__(self):
        super().__init__("send_recieve_serial_server")
        # parameter declcare
        self.declare_parameter("serial_number")
        self.declare_parameter("id_key")
        self.declare_parameter("timeout", .25)

        # get parameter
        self.serial_number = self.get_parameter("serial_number").value
        self.id_key = self.get_parameter("id_key").value
        self.serial_timeout = self.get_parameter("timeout").value

        # create server
        self.server = self.create_service(
            SerialMessage,
            "send_recieve_serial",
            self.callback_send_recieve_serial)

        # get serial port and connect
        self.get_serial_port(self.serial_number)
        self.connect_to_serial(self.serial_port)

    def callback_send_recieve_serial(self, request, response):
        try:
            start = time.process_time()
            # send message and recieve response
            recieved = self.send_and_receive(request.message)
            if recieved is not None:
                recievedKey = recieved[0]
                recievedData = recieved[1]
                # use response key to determine success
                if(recievedKey == request.success_key):
                    response.success = True
                    response.reply_data = recievedData
                    response.response_time = int((time.process_time() - start) * 1000)
                    self.get_logger().info("exchange succeeded in: {}ms".format(response.response_time))
                elif(recievedKey == "err"):
                    response.success = False
                    self.get_logger().error("MCU error: {}".format(recievedData))
                    response.reply_data = recievedData
                else:
                    self.get_logger().error("unknown response key: {}".format(recievedKey))
                    response.success = False

            return response
        except Exception as err:
            self.get_logger().error("exchange failed -> {} attempting reconnect...".format(err))
            self.serial.close()
            self.connect_to_serial(self.get_serial_port(self.serial_number))

    # return matching serial port for given serial number
    def get_serial_port(self, serial_number):
        match_port = None
        all_ports = serial ...
(more)
edit retag flag offensive close merge delete

1 Answer

Sort by ยป oldest newest most voted
0

answered 2021-08-08 12:24:37 -0500

Jaron gravatar image

I think this doesn't have to do with anything wrong you are doing with ROS2. Seeing as you're using a single-threaded executor, this really is just a simple service that reads from and writes to serial every time the service is called.

My next thought would be to debug physical connections or access issues. For instance, this link seems to sound very familiar to the issues you're seeing, especially with the port reassignment. If that doesn't help, try to debug further along the pyserial route. Hopefully this helps.

edit flag offensive delete link more

Comments

thanks for your comment, unfortunately I do not think it is the udev issue as udevadm monitor does not return anything in the current state where the addresses do not seem to be changing hardly at all but the timeout issue still occurs very frequently. I will try making the rule though if it seems that is happening.

I really want to agree that the issue is not on the ROS side but what makes it hard for me is that the service call patterns are the only way I can consistently cause and resolve the issue. The best example I can think of is that after the first failure and reconnection the messages continue to timeout as it seems that past service requests are filled, however once these complete and I stop calls to the service momentarily, then start again, it all begins working again (temporarily)

anyhow I will ...(more)

masynthetic gravatar image masynthetic  ( 2021-08-10 00:31:58 -0500 )edit

Question Tools

3 followers

Stats

Asked: 2021-08-07 17:45:10 -0500

Seen: 1,128 times

Last updated: Aug 08 '21