在准备过程中,实现了基于 rosserial 和 Arduino 的通讯,确定了单片机系统也可以作为 ROS 的一个 Node ,融入 ROS 机器人系统。
但按目前我的理解和水平,似乎 ROS 的 Master 必须在 PC 上运行,而且是安装了 ubuntu 相应系统的 PC。
如果通过串口线(USB线)和 Arduino 板相连才能实现 rosserial ,那局限性太大了。因为作为学习用的小车平台不会做的很大,否则成本太高。这样就无法将笔记本电脑放置在小车上,如果选择性能够跑 ROS 的单板电脑,似乎价格不菲。
所以,在实现 rosserial 后,第一步想验证的就是:可否将有线连接变为无线?
利用我自己做的两个USB无线适配器(可以支持USB转无线或者UART转无线,用 STM32F103C8实现,USB虚拟串口是用 STM32 的USB库实现的),一个通过USB连接到电脑上,一个通过UART口连接到 Arduino Nano 板。
因为我是在虚拟机上安装的 ubuntu,STM32 虚拟串口驱动安装在 win10 下 ubuntu 系统就应该能识别,实际上似乎有点问题,我 Linux 不熟悉,目前主要目的不是琢磨 STM32 的USB虚拟串口,所以绕开,用一个 CH340的 USB转TTL串口模块接入PC,这样在 ubuntu 中就认识了。
依次在 ubuntu 中启动终端,运行 roscore 、rosrun rosserial_python serial_node.py /dev/ttyUSB0 ,发现连接不上,显示同步失败。
https://github.com/ros-drivers/rosserial/blob/melodic-devel/rosserial_arduino/src/rosserial_arduino/SerialClient.py ;担心是 Arduino 的 USB 串口连接产生的复位信号没有,导致同步失败。用 USB转TTL串口模块直接接到 Arduino 芯片的 TXD、RXD端,结果正常工作,看来不是复位信号问题。
再次按照前述接线,用两个无线透传模块代替 UART 连线,这次成功了!
又试了几次,发现可能和复位有一定关系。
PC 上的 rosserial 运行必须在 Arduino 模块可靠复位后,否则似乎是由于串口缓冲中有数据,通讯的同步就无法实现。感觉库程序中有点问题,通讯帧的处理不能从连续的数据流中找到有效的同步帧,而是从复位开始检测。
这个以后在具体实施时再说吧,至少验证了通过无线实现 rosserial 的可能性,这样可以发挥的空间就大多了。
补白:
查阅了github 上的 roserial_arduino 源代码,是在请求通讯时通过 DTR 信号给 Arduino 板提供复位信号了:
https://github.com/ros-drivers/rosserial/blob/melodic-devel/rosserial_arduino/src/rosserial_arduino/SerialClient.py
……
import time | |
import rospy |
|
from std_srvs.srv import Empty, EmptyResponse |
|
from serial import Serial |
|
from rosserial_python import SerialClient as _SerialClient |
|
MINIMUM_RESET_TIME = 30 |
|
class SerialClient(_SerialClient): |
|
def __init__(self, *args, **kwargs): |
|
# The number of seconds to wait after a sync failure for a sync success before automatically performing a reset. |
|
# If 0, no reset is performed. |
|
self.auto_reset_timeout = kwargs.pop('auto_reset_timeout', 0) |
|
self.lastsync_reset = rospy.Time.now() |
|
rospy.Service('~reset_arduino', Empty, self.resetArduino) |
|
super(SerialClient, self).__init__(*args, **kwargs) |
|
def resetArduino(self, *args, **kwargs): |
|
""" |
|
Forces the Arduino to perform a reset, as though its reset button was pressed. |
|
""" |
|
with self.read_lock, self.write_lock: |
|
rospy.loginfo('Beginning Arduino reset on port %s. Closing serial port...' % self.port.portstr) |
|
self.port.close() |
|
with Serial(self.port.portstr) as arduino: |
|
arduino.setDTR(False) |
|
time.sleep(3) |
|
arduino.flushInput() |
|
arduino.setDTR(True) |
|
time.sleep(5) |
|
rospy.loginfo('Reopening serial port...') |
|
self.port.open() |
|
rospy.loginfo('Arduino reset complete.') |
|
self.lastsync_reset = rospy.Time.now() |
|
self.requestTopics() |
|
return EmptyResponse() |
|
def sendDiagnostics(self, level, msg_text): |
|
super(SerialClient, self).sendDiagnostics(level, msg_text) |
|
# Reset when we haven't received any data from the Arduino in over N seconds. |
|
if self.auto_reset_timeout and (rospy.Time.now() - self.last_read).secs >= self.auto_reset_timeout: |
|
if (rospy.Time.now() - self.lastsync_reset).secs < MINIMUM_RESET_TIME: |
|
rospy.loginfo('Sync has failed, but waiting for last reset to complete.') |
|
else: |
|
rospy.loginfo('Sync has failed for over %s seconds. Initiating automatic reset.' % self.auto_reset_timeout) |
|
self.resetArduino() |