[关闭]
@Pigmon 2021-08-09T16:36:26.000000Z 字数 10004 阅读 649

续写杨腾《NOX框架说明文档(临时)》

NOX


版本 日期 修改人 修改内容
V 20200220 2020年2月20日 袁胜 补全框架使用流程(3)的后半部分; 增加参数系统教程
V 20200224 2020年2月24日 袁胜 参数在程序中的使用; 参数配置文件的位置; 参数命名冲突
V 20200302 2020年3月2日 袁胜 添加第5部分(NOXAPK发布与部署);修改参数系统图1中命名冲突问题;
V 20200326 2020年3月26日 袁胜 添加第6部分 noxdriver;
V 20200326 2020年3月27日 袁胜 修复noxdriver中的程序错误;

3.(3) 增加一些处理细节

猴子的逻辑

在增加了Breeder模块中的banana信道传输的内容后,我们可以在Monkey模块中接收该信道的信息。跟Breeder模块一样,我们在Monkey.h中也重载一下Process函数:

  1. #pragma once
  2. #include ".MonkeyModule.h"
  3. namespace nox::app
  4. {
  5. class Monkey
  6. : public MonkeyModule
  7. {
  8. /// Override your process functions ...
  9. void Process(RecvMessage recv, SendMessage &send) override;
  10. };
  11. }

Monkey.cpp中的实现部分:

  1. void Monkey::Process(RecvMessage recv, SendMessage &send)
  2. {
  3. int cnt = recv.banana.data;
  4. Logger::I("Monkey") << "Get " << cnt << " bananas from Breeder.";
  5. }

然后回到工作区根目录,执行:

  1. noxmake
  2. noxrun

可以看到如下输出信息:

  1. [Information][Breeder]: Feed Monkey a banana.
  2. [Information][Monkey]: Get 1 bananas from Breeder.

以上就是为多个模块建立通信信道的最基本的流程。

4. 参数系统教程

参数组,参数和参数实例的关系如前文所述。我们现在来实践一下创建参数和使用参数的过程。
基本流程:

日常使用:

(1) 在Processon创建参数配置图

创建一个参数描述图:
param1_description2.png-14.4kB
这里,我们使用FlowChart流程图中的“数据库”图例来创建参数组,取名为 param_group1; 然后用类图中的“类”图例来创建参数,这里我们创建2个参数:Info 和 Lessons,里面的内容要符合 yaml 格式。
将改文件存储为.pos文件,保存在工作空间中的

  1. .nox/parameter/description

目录中。

创建参数dependence图:
param_dependence (1).png-11.2kB
该图描述模块和参数之间的引用关系。模块即上文中我们创建的 Breeder 和 Monkey,图中,我们让 Monkey 引用 Lessons 和 Info 两个参数,而 Breeder 只引用 Info 一个参数。
模块在这里还是使用UML类图中的“简单类”图例,而参数在这里使用 FlowChart流程图 中的“文档”图例。
将这个文件下载为.pos,放在工作空间中的

  1. .nox/parameter/dependence

目录中。

(2) 工作空间中的操作

刷新工作空间:
保存好两个参数配置图,回到工作空间根目录,执行:

  1. noxcreate refresh

执行完成后,检查工作空间的

  1. src/.param/template/header

目录,是否生成了两个参数对应的头文件,以及,检查

  1. src/.param/template/description

目录,是否有两个参数对应的yaml文件。
gen.png-11.6kB

在工作区根目录执行

  1. noxparam list

可以看到目前的所有参数:

  1. ParameterName @ IDName: Value1 Value2 ...
  2. - - - - - - - - - - - - - - - - - - - - - - -
  3. Info@param_group1:
  4. Lessons@param_group1:

可以看到每个参数的冒号后面都是空的,因为我们还没有参数组的副本。

创建参数组副本
执行:

  1. noxparam add param_group1 group1

为参数组 param_group1 创建一个名为 group1 的副本。
再次执行

  1. noxparam list

可以看到:

  1. ParameterName @ IDName: Value1 Value2 ...
  2. - - - - - - - - - - - - - - - - - - - - - - -
  3. Info@param_group1: group1
  4. Lessons@param_group1: group1

修改参数值
例如,我们想修改当前参数组副本中的 Info 的值,可以通过:

  1. noxparam edit Info group1

这样就打开了vim可以编辑Info参数的内容。如果想使用其他编辑器,可以用 -use 来指定:

  1. noxparam edit Info group1 -use nano

这样就是使用 nano 来进行编辑。

(3) 在程序中使用

头文件目录:
虽然参数的头文件已经生成,但是目前(2020年2月24日)该目录的引用需要手动配置。可以直接在 include 的时候写出全路径,也可以配置CMake。
例如,配置CMake:
打开 zoo 项目的CMakeLists.txt,在include_directories里添加Paramter.h所在的目录:

  1. include_directories(
  2. include
  3. ../.param/template
  4. ${catkin_INCLUDE_DIRS}
  5. )

使用参数配置:
我们在Breeder.cpp中,之前操作banana信道的地方,加上读取参数配置中的数值的部分。修改Process函数:

  1. void Breeder::Process(RecvMessage recv, SendMessage &send)
  2. {
  3. send.banana.emplace();
  4. send.banana.value().data = 1;
  5. /// -> 这里开始
  6. // 读取参数配置文件中的数值发送到banana通道
  7. nox::parameter::InfoParameter info_param;
  8. bool read_ok = info_param.Read("/home/pigmon/nox_demo/src/.param/instance/param_group1/group1/InfoParameter.yaml");
  9. if (read_ok)
  10. {
  11. send.banana.value().data = info_param.basic.age;
  12. }
  13. /// <- 这里结束
  14. Logger::I("Breeder") << "Feed Monkey a banana.";
  15. }

首先建立一个InfoParameter类型的对象,它针对的是我们前面加入的 Info 参数。然后使用nox::file::Parameter的Read接口来读取参数配置文件,进而得到参数配置中所有的值。
修改之后,回到工作空间根目录,执行

  1. noxmake
  2. noxrun

可以看到输出如下:

  1. [Information][Monkey]: Get 35 bananas from Breeder.

35即是group1实例中,Info参数中的age的数值。

(4) 文件路径:

参数组的描述文件:

  1. [工作空间根目录]/.nox/environment/[工程名].yaml

如:

  1. ~/nox_demo/.nox/evironment/zoo.yaml

这里是根据.pos文件生成的参数和参数组的关系描述,noxparam相关命令大部分的执行过程都是从读取这个文件开始。
参数组实例配置文件:
所有的参数组实例配置文件都在:

  1. [工作空间根目录]/src/.param/instance

目录下。
各参数头文件:
每个参数的头文件自动生成在

  1. [工作空间根目录]/src/.param/template/header/

下,但在

  1. [工作空间根目录]/src/.param/template

目录下会生成一个自动include这些头文件的 Parameter.h,每次参数配置变化执行 noxcreate refresh 以后,这个头文件也会更新,为了避免开发时频繁修改头文件,可以直接在工程中引用这个头文件。

(5) 参数和参数组命名

虽然参数组在制作和维护的时候是放在特定工程里的,但其实参数的可用范围是系统全局的,所以即便是在不同工作空间中,所有的参数和参数组都不能重名,否则会引起命名冲突。

5. 使用NOXAPK命令进行发布与部署

在日常使用NOX框架进行开发的过程中,我们经常会在开发用的电脑上编写程序,做好测试,然后将开发的结果部署到车辆的工控机器上。NOXAPK 是NOX框架中的命令行工具之一,其功能是将一个NOX工程的运行环境和可执行文件等必要内容进行打包发布,并可以在目标机器上进行部署。

(1) 例子说明

首先使用上面修改过的nox_demo工程,尝试一个最常见的操作:

作为准备,在开发机上,首先开启 roscore,然后新打开一个命令行。

(2) 打包发布 - noxapk create

先来到 zoo 项目所在的工作空间的根目录,在我的机器上,它是 ~/nox_demo:

  1. cd nox_demo

然后,使用 noxapk 命令对该工作区进行打包发布:

  1. noxapk create -min

因为我们只想打包必要的运行环境,所以这里使用 -min 参数,如果想指定发布文件的存储路径,还可以用 -output 参数进行指定:

  1. noxapk create -min -output [存储位置的绝对路径]

noxapk 命令的其他参数说明可以参考后面的表格。
这样,我们就得到了一个 [工作空间名].nox 文件,在我这里,它的名字是:nox_demo.nox。这就是我们发布出来的安装文件。

(3) 部署 - noxapk install

来到我们的目标机器上,打开一个命令行,来到我们想要部署该工作区的目录下。
然后执行:

  1. noxapk install nox_demo.nox -hard -not-make

因为我们这是第一次部署这个工作区,所以使用 -hard 参数,代表删除旧文件再部署(这里就是代表不考虑重叠覆盖的问题,使用其他参数也可)。
因为我们打包发布的内容中不包含源代码,所以也没必要再 make,因此加上参数 -not-make。
noxapk 命令的其他参数说明可以参考后面的表格。
执行这条命令后,可以看到它输出的:

  1. Begin to install apk ...

等这个进程结束,即代表部署完成。我们可以在当前目录查看一下有没有工作空间的目录。

(4) 测试

确保roscore启动,进入部署好的工作空间目录,我这里是 nox_demo 目录。
然后执行:

  1. noxrun

如果看到的输出和之前教程中的一样,就说明我们已经部署成功:

  1. Running List:
  2. 1. zoo/Breeder
  3. 2. zoo/Monkey
  4. [Information][Module]: --- START ---
  5. root: /home/pigmon/Downloads/nox_demo2/nox_demo
  6. package: zoom
  7. node: Breeder
  8. [Information][Module]: --- START ---
  9. root: /home/pigmon/Downloads/nox_demo2/nox_demo
  10. package: zoom
  11. node: Monkey
  12. [Information][Breeder]: Feed Monkey a banana.
  13. ...
  14. [Information][Monkey]: Get 35 bananas from Breeder.
  15. ...

(5) noxapk 命令参数说明

参数 描述 用法 执行目录
create 为NOX工作空间创建APK包
-output 指定apk的存放目录 create -output [output directory] [workspace_root]
-min 打包最小集,只包括启动工作空间的必要文件 create -output [output directory] -min [workspace_root]
-all 打包最大集,包括所有文件 create -output [output directory] -all [workspace_root]
-full 开发中的选项,包括必要文件和源文件 create -output [output directory] -full [workspace_root]
-f 即使Make出错也强制打包 create -output [output directory] -[min/all/full] -f [workspace_root]
install 将APK安装到NOX工作空间
-none 保留旧文件且不更新 noxapk install [apk_name] [installed directory] -none [workspace_root]
-soft 只更新重叠文件 noxapk install [apk_name] [installed directory] -soft [workspace_root]
-cover 用新文件覆盖旧文件 noxapk install [apk_name] [installed directory] -cover [workspace_root]
-keep 保留被覆盖的旧文件 noxapk install [apk_name] [installed directory] -keep [workspace_root]
-hard 删除旧文件并更新 noxapk install [apk_name] [installed directory] -hard [workspace_root]
-exclude 只保留重叠文件 noxapk install [apk_name] [installed directory] -exclude [workspace_root]
-not-make 安装完成后不设置工作空间 noxapk install [apk_name] [installed directory] -[none/soft/cover/keep/hard/exclude] -not-make [workspace_root]

6. 使用NOX Driver配置车辆的CAN协议

NOX Driver是NOX框架中处理车辆CAN驱动的部分。它可以根据开发人员编写的CAN协议配置文件,自动生成CAN报文收发和处理的程序框架,之后开发人员只需补充业务逻辑的代码,而复杂的报文解析合成的处理由自动生成的程序完成,大大的节省了车辆CAN报文部分开发处理的时间。

6.1 用户操作步骤说明

1.png-162.2kB
图6.1 NOX Driver运行流程

开发人员使用NOX Driver对CAN驱动进行配置的工作主要分为以下3个部分:

以下对步骤进行操作说明。

6.1.1 配置报文内容

描述报文内容的配置文件为 yaml 格式,格式描述如下:

  1. %YAML:1.0
  2. devices:
  3. - name: <device_name>
  4. interface: <interface_name> | [<interface_name>]
  5. frames:
  6. - name: <frame_name>
  7. id-recv: <id/id_bool_expr> | [<id/id_bool_expr>]
  8. id-send: <id/id_cal_expr>
  9. id: <id>
  10. type: standard | extended | remote | error
  11. fields:
  12. - name: <field_name>
  13. type: double | bool | string | int |
  14. start: <bit_number>
  15. length: <bits_count>
  16. format: intel | motorola
  17. resolution: <data_resolution>
  18. offset: <data_offset>
  19. min: <data_ minimum>
  20. max: <data_maximum>
  21. signed: false | true

上述内容仅为一个示意,不是标准YAML语法,其中“|”代表“或”,表示隔开可能的多种取值;“<…>”表示一个变量,由开发人员填写;“[…]”表示YAML的数组。在本框架特有的CAN数据协议格式中,涉及到的各个字段含义及其取值范围如下:

字段名 字段含义 字段变量名 变量含义
devices CAN设备数组
devices/name CAN设备名字,将作为C++类名 device_name 自定义的名字,需要符合C++变量命名标准
interface CAN设备接口名字(可选,可为数组) interface_name CAN设备接口名字,从操作系统获得
frames CAN设备包含的所有CAN帧信息
frames/name 帧名,将作为C++类名和函数名的一部分 frame_name 自定义的名字,需要符合C++变量命名标准
id-recv 本帧从CAN设备接收的报文ID(可选,可为数组) id/id_bool_expr 一个整数,或一个含变量id的布尔表达式,用以在线匹配
id-send 本帧发送到CAN设备的ID(可选) id/id_cal_expr 一个整数,或一个包含变量id的计算表达式,用以在线计算发送的真正ID
id 本帧接收和发送用的ID,会被id-recv或id-send对应覆盖(可选) id 整数
type 指定本帧的格式 standard | extended | remote | error 标准帧,扩展帧,远程帧,或错误帧
fields 本帧包含的所有字段的数组
fields/name 字段的名字,将作为变量名 field_name 自定义的名字,需要符合C++变量命名标准
start 字段起始位 bit_number 一个整数(0-63)
length 字段的位长度 bit_count 一个整数(1-64)
format 字段的格式 intel motorola
resolution 字段数据的分辨率(可选,默认为1) data_resolution 一个实数
offset 字段数据的偏移量(可选,默认为0) 一个实数
min 字段数据的最小值,用于限制最小发送值(可选,默认无限制) data_minimum 一个实数
max 字段数据的最大值,用于限制最大发送值(可选,默认无限制) data_maximum 一个实数
signed 字段数据的符号性(可选,默认无符号) false | true 无符号或有符号

6.1.2 生成CAN处理程序

编写好yaml文件后,使用 命令进行生成:

参数 描述 用法 执行目录
[yaml_file] yaml配置文件名 nox-can-driver-gen [yaml_file] (output [out_path]) 任意目录

output [out_path] 为可选输入,缺省在当前目录。

6.1.3 部署工程

将生成的工程放入某个NOX工作空间,即可通过 Noxmake 进行编译。

6.1.4 添加业务逻辑代码

在生成的协议处理程序框架中,分别重载收发部分的处理函数,详见后面操作实例。

6.1.5 运行

启动CAN接口使用 noxdriver 命令:

命令 描述 用法 执行目录
noxdriver 启动CAN接口node noxdriver [project] [node] --socketcan [can_port] [workspace_root]

例如:

  1. noxdriver driver tongli --socketcan can0

6.2 用户操作步骤实例

6.2.1 编写配置文件

这里我们从一个简单的,有收发各一个报文的协议开始,配置文件内容如下:

  1. %YAML:1.0
  2. #--------------------------------------------------------------------------------------------
  3. # Write your description ...
  4. #--------------------------------------------------------------------------------------------
  5. devices:
  6. - name: Chassis
  7. frames:
  8. - name: GearSend
  9. id-send: 0x66
  10. type: standard
  11. fields:
  12. - {name: gear_state, type: int, start: 0, length: 3, format: intel}
  13. - name: GearRecv
  14. id-recv: 0x67
  15. type: standard
  16. fields:
  17. - {name: gear_state, type: int, start: 0, length: 3, format: intel}
  18. - {name: is_driver_override, type: bool, start: 3, length: 1, format: intel}
  19. - {name: reported_gear_state, type: int, start: 4, length: 3, format: intel}
  20. - {name: is_canbus_fault, type: bool, start: 7, length: 1, format: intel}

我们新建一个名为Chassis的协议,其中发送给车端CAN的报文为 GearSend,接收车端的报文为 GearRecv,报文ID分别为 0x66 和 0x67。
文件命名为 test_dgram.yaml。

6.2.2 生成工程

如上文描述,使用 nox-can-driver-gen 命令来根据上面的配置文件生成工程。

  1. nox-can-driver-gen test_dgram.yaml

之后我们可以看到,在当前目录下,生成了一个 test_dgram 目录,目录内部结构即为典型的 catkin 工程,包括 src 目录和 Package.xml 以及 CMakeLists.txt。src目录下可以看到生成的源程序:

noxdriver1.png-9.6kB

其中,Chassis.h\cpp 是我们主要需要进行业务逻辑编写的地方。

6.2.3 部署生成的工程

将生成的 test_dgram 目录拷贝至我们之前使用的 nox_demo 工作空间的 src 目录中,然后执行:

  1. noxmake

编译结束,可以在 /home/pigmon/nox_demo/src/.plugin/lib/test_dgram/ 目录下看到库文件 test_dgram.so,即说明编译成功。

注6.1 :2020年3月27日为止,项目名必须改成driver。今后会修复。

6.1.4 添加业务逻辑

要添加接收到CAN报文,和发送CAN报文的业务逻辑,我们需要重载 ChassisModule 类中定义的虚函数,首先以接收到报文的处理函数为例。
我们在生成的 Chassis.h 中添加 ProcessGearSendFrame 函数的重载声明:

  1. #pragma once
  2. #include <drivers/.ChassisModule.h>
  3. namespace nox::driver
  4. {
  5. class Chassis :
  6. public ChassisModule
  7. {
  8. /// Override your process functions ...
  9. void ProcessGearRecvFrame(const std::string & name, unsigned int id, GearRecvFrame frame) override;
  10. };
  11. }

然后在 Chassis.cpp 下实现该函数:

  1. void Chassis::ProcessGearRecvFrame(const std::string & name, unsigned int id, GearRecvFrame frame)
  2. {
  3. Logger::I("Chassis") << "Recieved " << id << " with gear " << frame.gear_state;
  4. }

在 ChassisModule 类中,我们可以看到 GearSendFrame 的定义和实现,报文中的字段都定义成了通用类型以供读写,除此以外,ToCANMessage 和 FromCANMessage 中自动生成的程序已经实现了参数化的报文转换功能,这部分已经不需要我们自己来实现了。

发送报文的部分,我们只要调用 SendGearSendFrame 函数即可。

6.1.5 测试

启动刚刚实现的CAN协议处理程序,需要使用 noxdriver 命令,其基本格式是:

  1. noxdriver [project] [node] -socketcan [can_port]

这里我们的启动方式(潍柴车)是:

  1. noxdriver driver driver -socketcan can2

参考 注6.1
可以看到车辆输出:

  1. [Information][Chassis]: Recieved 103 with gear 4
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注