摘要:MIT Cheetah-Software是四足机器人的开源项目,理清程序流程是进一步理解和研究项目的必要条件。Cheetah-Software开源项目包含robot、common、sim和user等四个部分的源码,本文以user/MIT_Controller下的子项目来介绍Cheetah-Software的程序流程。
主函数定义在main.cpp文件中,源码如下:
int main(int argc, char** argv)
{
main_helper(argc, argv, new MIT_Controller());
return 0;
}
在主函数中,先使用new生成一个MIT_Controller对象,然后调用main_helper函数,最后返回0,流程如图1所示。
图1 主函数流程图
注:MIT_Controller在user/MIT_Controller/MIT_Controller.hpp文件中。
main_helper函数定义在robot/src/main_helper.cpp,流程图见图2。
图2 main_helper函数流程图
上面流程图中,大部分流程用于命令参数判断,在流程图后面三个分支中的对象是我们研究的重点,即MiniCheetahHardwareBridge、CheetahHardwareBridge和SimulationBridge三个类的对象声明hw和hw.run()。
MiniCheetahHardwareBridge和CheetahHardwareBridge是HardwareBridge的子类,是两种不同类型的四足机器人对象,而SimulationBridge类是仿真模拟类,用来仿真四足机器人对象,下面分别以MiniCheetahHardwareBridge和Cheetah3HardwareBridge为例来介绍四足机器人的运行流程。
HardwareBridge类是一个抽象类,定义了run()的接口函数,其子类在run()中实现机器人运行代码。
MiniCheetahHardwareBridge::run()的流程图如图3所示。
图3 MiniCheetahHardwareBridge::run函数流程图
1、初始化通信模块,调用基类HardwareBridge::initCommon()函数,通过LCM通信模块订阅interface和interface_request两个主题;
2、初始化MiniCheetah机器人的spi通信和惯性导航模块(IMU);
3、初始化机器人参数和用户控制参数列表;
4、创建和配置RobotRunner对象(实际上是一个任务对象);
5、启动状态任务;
6、启动spi通信任务,spi通信负责传输控制命令和接收传感器数据;
7、启动惯性导航线程;
8、执行RobotRunner的任务;
9、启动可视化数据LCM通信任务;
10、启动遥控器指令接收任务;
11、启动惯性导航日志任务;
12、每隔1秒循环。
Cheetah3HardwareBridge::run()的流程图如图4所示。
图4 Cheetah3HardwareBridge::run函数流程图
1、初始化通信模块,调用基类HardwareBridge::initCommon()函数;
2、初始化Cheetah3的硬件系统;
3、初始化机器人参数和用户控制参数列表;
4、创建和配置RobotRunner对象;
5、初始化RobotRunner对象;
6、启动状态任务;
7、启动工业以太网(基于ethercat协议);
8、执行RobotRunner任务;
9、启动可视化数据LCM通信任务;
10、每隔1秒,打印状态。
在Cheetah3HardwareBridge::run()的流程中调用了RobotRunner::init()和RobotRunner::start(),下面分析两个函数的流程和功能。
RobotRunner::init()的流程如图5所示。
图5 RotbotRunner::init函数流程图
1、根据机器人类型创建四足机器人模型(_quadruped)和生成基础移动模型;
2、创建机器人关节位姿初始化对象;
3、创建腿控制器对象;
4、创建状态预测容器并初始化;
5、创建期望状态命令对象;
6、将生成的对象赋值给机器人控制对象;
7、初始化机器人控制对象。
RobotRunner的基类是PeriodicTask,PeriodicTask是一个线程任务类,所以RobotRunner::start()实际调用的是PeriodicTask::start()。RobotRunner::start()的流程如图6所示。
图6 启动RobotRunner任务流程图
1、调用PeriodicTask::start();
2、PeriodicTask::start()启动线程,线程的主体函数是PeriodicTask::loopFunction();
3、在PeriodicTask::loopFunction()中循环执行PeriodicTask::run()函数,而PeriodicTask::run()是一个纯虚函数,实际调用的是RobotRunner::run()函数。
RobotRunner::run()是机器人系统每个执行周期的运行内容,其流程如图7所示。
图7 RobotRunner::run函数流程图
1、运行状态预测;
2、可视化数据清零;
3、将机器人数据更新到LegController对象;
4、设置LegController对象使能;
5、当遥控器控制时的rc_control.mode为0时,将LegController对象的控制命令数据清零,否则执行6;
6、如果关节位置初始对象没有初始化时,将LegController对象的各个关节的kp和kd赋值,否则执行7;
7、执行机器人控制对象(实际是MIT_Controller对象)的runController()和updateVisualization()函数。
RobotRunner::setupStep()的功能是将机器人的各关节数据更新到LegController对象,流程见图8所示。
图8 RobotRunner::setupStep函数流程图
MIT_Controller::runController()的功能是根据机器人当前位姿、期望位姿、遥控器指令、步态等多种数据计算各关节的控制命令,流程见图9所示。
图9 MIT_Controller::runController函数流程图
RobotRunner::finalizeStep()的功能是将生成的控制命令数据更新到机器人各控制系统,流程见图10所示。
图10 RobotRunner::finalizeStep函数流程图
本文从/user/MIT_Controller/main.cpp的main()函数出发,通过逐次递进的方法分析了MIT开源项目Cheetah-Software机器人系统运行的主要流程,这只是源码分析的开始,希望和四足机器人爱好者们共同进步!