我们在ROS使用的过程当中,上位机ROS系统和单片机之间的通讯控制是在所难免的。目前大多数是基于ROS-Serial,又或者是ROS和STM32的库,而且目前大部分ROS机器人的驱动板均为STM32系列,Arduino的也有,但是对于熟悉Arduino却不熟悉C++的小伙伴来说,仍存在较大的难度。我本人是做嵌入式软件的,硬件层次对于Arduino十分热爱,Arduino的语法在我看来就是C语言,而并不是羞涩难懂的C++(PS:至少对于初学者看来是如此)。

 

在一次偶然的机会下,我接触到了Arduino的Firmata通讯协议,它保证了Arduino语法在其他语言应有的风格,目前对C#和Python支持较为完善,如果你需要在Python或者C#代码里面实现和Arduino的通讯,Firmata协议完全是你的上上之选。

 

在机器人的设计当中,软件系统和硬件之间的交互是必不可少的,ROS系统是目前主流的机器人系统,对于Python也有着强大的支持。这个内容是我个人对于ROS和Arduino通讯的认知,如有不完善或者错误的地区,望大牛多多指出,不喜勿喷。

 

Firmata官网:http://firmata.org/wiki/Main_Page

 

Firmata是一个PC与MCU通讯的一个常用协议。其遵旨是能与任何主机PC软件包兼容。到目前为止,已经得到不少语言的支持,这样可方便地将对协议的支持加入软件系统中。Firmata起初是针对于PC与Arduino通讯的固件(Firmware),其目标是让开发者可以通过PC软件完全地控件Arduino。

 

Firmata是一套基于Arduino通讯的API接口,方便软件端高速、便捷、有效的来实现我们对底层控制应用,大家在通讯的过程,经常会碰到关于数据编码问题、格式问题等麻烦点,虽然可以解决,但是较为耗时耗力。我们先来看一下C#对于Firmata协议控制Arduino作为下位机的API函数。

C#与Firmata协议接口
 
 

在这里我以API函数名称和部分函数实现来作为演示,下面是我在C#调用Firmata协议的实例。

C#使用Firmata API实例
 
 

语法和Arduino完全相同,如果你熟悉C#和C语言,同时有Arduino基础语法的话,很容易就可以设计一个上位机或者是工控机软件(基于Arduino)。

我们在这里的主体是ROS,我们接下来会在ROS+Python,通过Firmata API,实现ROS和Arduino的交互。

我的设计是在ROS下,Python调用Firmata API接口,将订阅到的数据让Arduino执行,又或者是从Arduino获取数值,发布到我们的ROS里面。

我们的编程语言是Python,我们需要来了解一下Python和Firmata的接口API是怎么样的。有关Firmata的内大家可以在百度上面找到更加详细的内容。

 

Python Firmata API
 
 

我们需要对Arduino开发板烧录一下StandardFirmata的固件程序。打开我们的Arduino IDE,文件→示例→Firmata→StandardFirmata,选择好Arduino型号及端口,下载即可。如果你是在Linux下操作,记得给端口一个权限。

StandardFirmata
 
 

示例程序的波特率默认是57600bps,8N1模式。有需要的可以自行在setup函数进行手动更改。

 

接下来是我们在Ubuntu下操作,我使用的是Ubuntu 18.04.5 + Melodic,默认使用Python2。我们需要先安装Python对于Firmata的第三方功能包,在终端输入pip install pyfirmata即可。如果你想在Python3下使用,则是pip3 install pyfirmata。当然,如果你的Python环境变量有修改的情况下,以你的Python环境变量为准。

 

在这里我使用的是Arduino UNO开发板,57600bps,8N1模式。Arudino UNO有6路模拟值输入口(A0、A1、A2、A3、A4、A5);12路数字值输入输出口(D2、D3、D4、D5、D6、D7、D8、D9、D10、D11、D12、D13),其中D3、D5、D6、D9、D10、D11具有模拟值输出功能(PWM),D2和D3具有外部中断功能(INT0和INT1),D0和D1为通讯串口,不作为普通IO口使用。

 

我们先来建立一个learning_firmata功能包,我们这个功能包是Python语言为主,所以要依赖rospy,为了避免后期有用到C++的地方,我们把roscpp依赖也给添加,同时把std_msgs的ROS标准数据格式也给添加一下(roscpp和std_msgs可能用到,也可能用不到,添加一下吧,关于std_msgs的解释是我个人认知,可能存在不准确性)。我们打开一个终端,执行catkin_create_pkg learning_firmata rospy roscpp stdmsgs即可创建learning_firmata功能包,然后回到我们的工作空间执行catkin_make进行编译。

 

创建功能包
 
 

 

关于catkin_make和source得内容我都不截图了哈,玩ROS这是必须得会的。

 

我们先来创建msg文件文件,用来存放我们的话题文件,这一节我们先来实现模拟值输入得读取,并在rqt绘制出折线图。

 

我们先来创建一个msg文件夹,同时在这个文件夹下创建一个Adc.msg用来订阅话题得内容。Adc.msg内容如下。

 

Adc.msg
 
 

 

我们接下来需要配置package.xml和CMakeList.txt。我们这次是一个小的Demo,没有用到其他的功能,只需要在原来的基础想增加message_generation编译依赖和message_runtime运行依赖就可以,我的package.xml内容如下。

 

package.xml
 
 

 

然后是我的CMakeList.txt文件,我们需要在find_package添加message_generation,在add_message_files添加我们的Adc.msg,在generate_messages添加我们的功能包名称learning_firmata即可。我的CMakeList.txt内容如下。

 

CMakeList.txt
 
 

 

我们现在可以来编译一下,然后source生效环境变量,使用rosmsg show learning_firmata/Adc来查看一下当前消息的格式。如下。

 

rosmsg show查看
 
 

 

到目前为止,我们已经做好了所有的准备工作,接下来就是写我们的ros节点文件。我们在learning_firmata功能包下创建一个scripts文件夹来存放我们的Python代码,并建立python文件pub_adc.py,然后使用sudo chmod +777 pub_adc.py赋予该文件最高权限。这个位置一定要赋予权限,不然你在rosrun运行的时候会找不到这个文件。Python的代码文件就是执行文件,如果没有可执行权限,那么Ubuntu默认它是一个文本。我的pub_adc.py内容如下。

 

pub_adc1
 
 

 

pub_adc2
 
 

 

我们这个节点名称叫做Arduino_ADC_Publisher,发布的话题名称为Arduino_ADC。现在我们来运行一下,看一下效果。商首先在终端启动roscore,然后是rosrun learning_firmata pub_adc.py。可以看到下面的效果。

 

启动pub_adc.py
 
 

 

然后是启动rostopic list来查看一下。

 

rostopic list查看
 
 

 

可以看到,我们的Arduino_ADC的topic已经发布,你可以通过rostopic echo /Arduino_ADC来查看,这里我不做展示。

 

然后是启动我们的rqt_plot看一下是否可以显示呢。重新打开一个终端,输入rqt_plot,效果如下。

 

出错
 
 

 

糟糕,出错了!经过检查发现,我们的Firmata读取的数据是float类型的,而我们定义的缺少uint16类型,那么我们现在去修改一下Adc.msg里面的数据类型,改为float64吧。然后我们重新编译运行,大功告成!

 

rqt_plot
 
 

 

最后我们来写个launch文件收尾,在learning_firmata功能包下,创建一个launch文件夹存放我们的launch启动文件,在该文件夹下,创建pub_adc.launch文件,内容如下。

<launch>
<node name=”Arduino_ADC_Publisher” pkg=”learning_firmata” type=”pub_adc.py” />
</launch>

 

就是声明下节点名称,说明这个节点是哪个包的,启动的是什么东西,比较容易理解。

 

关于ROS使用Firmata API,在我看来更像是Python在使用,是是Python把从Firmata API获得数据在转到了ROS下面。ROS是一个很好的工具,它的通讯模式绝对是一个优秀的框架,我们作为开发者在更多的程度上,是将自己的设计尽量的去靠近ROS,同时也方便我们来使用ROS下现有的、强大的第三方功能包,我们的设计依赖于ROS。

 

接下来我也会继续测试其他内容,静待更新。内容语法可能存在错误,欢迎大家指点交流。