示例目标:用户在小程序单击navigation_goal_按钮后,把机器人导航到目标物“moveit”,并操作机械臂夹起目标物。

1、(小程序)navigation_goal_:set_did_navigation_left_click("moveit", "__ROSE_MOVEIT");
set_did_navigation_left_click用于挂接当用户单击navigation_goal_按钮后,会执行什么操作。针对示例,一旦单击按钮,将使机器人开始导航,导航到目标物标识“moveit”绑定的坐标、角度。第二个参数“__ROSE_MOVEIT”是个保留字符串,表示导航成功后,要执行机械臂操作。
第二个参数可以设为一个lua函数,表示导航成功后执行这个lua函数。要控制某个蓝牙智能家居,就用这种方法。
2、某一时刻,用户单击navigation_goal_按钮,执行ros_instance_.moveto_guid(object_id, bh, luafunc)
参数object_id是目标物标识“moveit”,bh是导航成功后行为类型,此处是navigation_bh_moveit,表示操作机械臂。在navigation_bh_moveit类型时,第三个参数luafunc固定是NULL。
当set_did_navigation_left_click的第二个参数是lua函数时,bh将是navigation_bh_luafunc,第三个参数luafunc指示了要执行的lua函数。
2.1 判断当前条件是否满足导航要求,不满足就报错,报错后结束此次导航
- 必须设置了当前地图。
- 必须已把当前小程序的目标物标识“moveit”绑定到了当前地图的某个位置。并找到这个位置,位置的uuid标识存储在栈内变量uuid。
- 必须已设置有效的底盘驱动、雷达驱动,并且它们不能用同一个串口。
- bh是操作机械臂时,必须设置了有效的机械臂驱动,机械臂驱动包括一个moveit_model,这模型包括urdf、srdf、zx查找表(moveit.rsp)。
2.2 生成任务task_ptr(类型ttask*),并注册到系统
生成任务时,需要知道导航要到达的坐标+角度,这是通过位置标识uuid找到tmap_position,tmap_position存储着这两个信息。
注册是通过register_slot。register_slot会按需把tros_instance变换到started状态。
3、弹出“地图”界面,在app_post_initialize调用moveto_guid_bh
为什么要弹出“地图”?——操作机械臂时须要使用摄像头,目前使用摄像头需要一个track控件,这里用了“地图”界面中的track。
“地图”窗口类型是场景,初始化完成后会调用app_post_initialize,也就是说,在初始化完成后调用moveto_guid_bh。
bh是执行lua函数时,不须要track控件,因而在navigation_bh_luafunc类型下,是在moveto_guid未尾时调用moveto_guid_bh。
不论哪种bh,接下是进入moveto_guid_bh。
4、执行阻塞式任务:moveto_function
- 执行start_navigation_node,启动导航相关节点
- 阻塞,直到move_base节点已启动。move_base节点是在新线程运行的, 这里须同步下它和主线程。
- 执行do_send_goal,它有两个任务。1)机器人存在机械臂时,把机械臂设置到“navigation”状态。机械臂驱动保证把“navigation”状态对应到最适宜导航。2)调用sendGoal方法,向move_base节点发送导航目标。
sendGoal后,moveto_function就结束了。在其它节点线程,像move_base节点,将开始导航工作。moveto_function是作为阻塞式任务被执行,要求有一个bool返回值,它总是返回true。
5、让task_指向栈内变量task_ptr
task_是tros_instance成员,这样后面逻辑就可以从task_拿到这个任务了。task_ptr也将由tros_instance负责释放。
至此,用户在小程序单击navigation_goal_按钮,导致执行C++层的ros_instance_.moveto_guid方法,moveto_guid执行完毕。后面是相关导航节点进入操作,主线程则等待它们反馈,跟据反馈执行相应操作。在这里,中间过程就不详述了,直到导航结束时,move_base节点会发来导航结果,看主线程如何处理这个结果。
1、tros_instance::did_move_base_result
不论导航成功还是失败,move_base都会发出“move_base/result”话题,指示导航结果。tros_instance收到这话题,调用did_move_base_result。
- 调用各tslot的did_move_base_result方法。“地图”是当中一个slot,发现导航成功,而且导航后行为是操作机械臂,于是调用tcamera_rfinder::start_pickplace。并返回true,使得变量keep_task是true,即要求tros_instance::did_move_base_result继续保留task_。
- 导航后行为是navigation_bh_luafunc时,执行小程序层函数luafunc。如果keep_task是false,销毁task_。对操作机械臂,“1.1”已写了,不销毁task_,后面会看到,要等操作机械臂结束后,才会销毁task_。
如果导航失败,或导航成功后不是操作机械臂,那到现在,task_已销毁,此次小程序导致的导航已结束。
tcamera_rfinder::start_pickplace会指示tcamera_rfinder模块开始操作机械臂,它不是个同步操作,即不是操作完机械臂了再返回。在tcamera_rfinder模块,用了一个状态机,状态变量pickplace_step_。平时状态是空闲(nposm)。start_pickplace会启动摄像头,因为摄像头绑在机械臂,于是须要把机械臂设置到“camera”状态。机械臂驱动保证“camera”状态对应到最适宜拍摄正前方,设置完机械臂后,进入step_lookupzx状态。
2、step_lookupzx状态
此个状态下,tcamera_rfinder执行lookup_zx方法。首先调用目标物小程序的get_geometry方法得到个taplt_object_geometry结构,当中height字段指示了希望机械臂爪子要到达的高度。有了这个高度后,把它作为z,在moveit_model的zx查找表找到x、以及要达到这个zx、手臂group中各关节的关节值joints。
得到x和joints后,状态机进入step_finetune状态。
3、step_finetune状态
摄像头进入测距模式(task_range)。测距总逻辑是算出现在机器人离目标物的直线距离,然后和x比较,相差太大就移动机器人,太远让靠近目标物,太近就远离目标物。不断重复这个调整过程。如果测距成功,机器人会“站”在一个距离值接近x值的位置。
测距成功后,摄像头恢复到空闲模式,tcamera_rfinder进入step_pick状态。
只要没成功,测距一直会进行。要退出测距只有两种方法,一是成功,二是取消此次操作机械臂。
4、step_pick状态
在此个状态下,tcamera_rfinder执行pick方法。机械臂按step_lookupzx找到的joints控制手臂group各关节,以及控制爪子group关节。操和完后成后,关闭摄像头,tcamera_rfinder状态机进入空闲(nposm)状态。
在tcamera_rfinder模块,不论是此处的成功操作机械臂结束,还是失败,像测距时取消,tcamera_rfinder末尾都会调用app_pickplace_finished方法。它是个虚方法,“地图”就重载了它,内中会销毁task_。
至此,此次小程序导致的导航全部结束。