主逻辑
主逻辑为:找球——>定位——>击球。
找球
基本思想
找球的过程可分为两步,即在场地找球和找到球之后走到球附近。
初级版采用地毯式搜索在场地找球,即走一段停下来然后在原地摇头找球,找到则调整身体,使身体正对着球,并走到球附近,否则继续往前走。
找到球后的行走过程中,机器人会不断的看球确认,一旦发现其身体方向并没有正对着球,则停止行走,并再次调整身体,直到走到指定位置。
python程序实现
1 | def walkToBall(self, min_ball_d=0.35, max_ball_theta=0.15): |
程序说明
首先调用看球函数findBall()
进行一次找球,如果找到则跳到下面的If
判断,否则返回False
,进入If
结构,即调用摇头找球函数moveheadTofindBall()
进行摇头找球,如果还未找到则继续往前走。
找到球后,首先根据返回的ball_Info
属性(ball_info = [ball_x, ball_y, ball_theta])
的第3个值,调整身体位置,然后进入While True
循环。
进入该循环前,首先调用moveTo()
函数,并调用其post
属性使其进程挂起,即在行走的过程中,可以做其他事情。为了防止机器人的行走有误差,可将moveTo()
函数的x
参数加入一个系数,以确保机器人一定能走到球附近。
在行走的过程中,机器人会每隔0.5s看一次球,并通过返回的ball_Info
信息来判断是否到达指定位置和身体是否偏移球的位置。
由于到达指定位置的优先级大于是否偏离球的位置,所以将是否到达指定位置的判断放在前面,即不管身体是否偏移,到达位置即可,(后续会再看一次球,调整回来)。
如果未到达指定位置,但身体没有偏离到最大偏差角,即继续执行,一旦偏离最大偏差角,则停止行走,并立即调整身体位姿,并重新调用之前的moveTo()
函数。
注:在距离球越近时,机器人应根据距离调整低头的角度,以确定球在视野范围内。在进入走到球附近的程序中,如果没看到球,会进行一次小范围摇头找球,以确定机器人是以看到球的基础上进行校正。
定位
三角定位
基本思想
定位的主要目的即让机器人调整至最佳击球位置。由于我们采用的是右手握杆击球,即球杆,球和球洞在同一条水平线上。
由于机器人到达球的附近时,并不一定是最佳击球位置,所以定位是高尔夫比赛中必不可少的一个环节。初级版采用的是三角定位的方式,即调整机器人的位姿,使得机器人、球和球洞之间的三角形为直角三角形。
如图2所示,红色圆点为球的位置,蓝色圆点为机器人的位置,黄色为黄杆(球洞)。机器人-球-球洞之间的夹角为α,机器人-球洞-球之间的夹角为β,球-机器人-球洞之间的夹角为γ(已知),机器人和球的距离为d(已知),球和球洞的距离为l。
此时由于α为锐角,而最佳击球位置为直角,所以机器人应该向左绕一个以球为圆心,夹角为θ(θ = 0.5 * pi - α)的圆弧。并且此时应用反手击球。同理可得,如果α为钝角,θ = α - 0.5 * pi,机器人向右绕圆弧。
如果机器人在另一侧,方法同上,但击球方式应为正手击球。
但此时存在一个问题,只有黄杆上面的landmark(如图1所示),机器人才能识别出距离,但只有在1米的有效距离内识别出,而机器人可以在很远的距离内识别出黄杆,但无法返回距离。
所以,在远距离时,只能利用黄杆来定位,但在机器人、球和黄杆组成的三角形中,由于已知的信息较少,无法正确解出三角形,因此我们必须假设一个信息是已知的。
我们假设β是已知的,并且球和球洞的距离越远,该角度越小。利用这个假设,我们便可以让机器人通过
moveTo()
函数,使得到达最佳位置,即机器人-球-球洞的夹角为0.5pi。
python程序实现
1 | def moveCircle_stick(self, stickAngle, ball_d, compensateAngle1): |
程序说明
首先计算出图2的α,用来判断是锐角还是钝角,其次需要判断机器人和黄杆的角度,如果为正值,应该为正手击球,否则应为反手击球。
在moveTo()
的函数中,需要给定的参数为y和θ的值。其中y的值可以用余弦定理求得,而θ为0.5pi与α的差值的绝对值。
注:机器人以左手为y坐标系正半轴,正前方为x坐标系正半轴,设置moveTo()
函数的参数时,要注意正负号。
图像定位
基本思想
经过三角定位后,机器人基本上到达最佳击球位置,但是我们的最佳击球位置不是(0,0),而是(0.20,-0.05),所以经过三角定位后,还需要左右和上下平移。
之前的方法为利用看球后返回的x和y轴的信息,然后调用moveTo()
函数,不断的调整,但实际测试下来,效果并不理想,主要有2个原因。
第一如果机器人不是正对着球,看球的信息会不太准确,而且由于此时要求精度较高,该误差较大。第二机器人本身精度也有误差,特别是此时只是微小移动,很难快速收敛。
针对以上问题,我们决定直接利用球在图像中的信息进行调整,即不需要在进行球的返回信息(x和y)计算,而且此时球在图像中的像素值很大,加入一个比例控制系数就会很快的收敛。
python程序实现
1 | def locateWithImage(self, best_ball_x=400, best_ball_y=320, best_ball_radius=28): |
程序说明
首先要测得最佳击球位置在图像中的像素位置,然后根据实际值与期望值的差得到误差,注意此时图像和机器人的坐标系不一样,然后每次调整位置时,加入一个比例系数Kp,实验测得,经过3-4次调整即可快速的收敛,到达最佳击球位置。
注:三角定位要和图像定位结合使用,三角定位是为了调整机器人与球洞的角度,而图像定位是为了让其到达指定的(x,y)位置,为了更精准的到达指定位置,每次循环进行1次三角定位和2次图像定位。实验测得,经过3次左右的循环,便可完成定位,而且其效果也比较理想。
击球
基本思想
考虑到机器人与球洞的位置,我们设计了2种击球方式,即正手击球和反手击球。
其方向可以通过黄杆或landmark的角度来判断。当角度为正值时,说明球洞在机器人的左手边,由于我们采用右手击球,所以应为正手击球,否则应为反手击球。
python程序实现
1 | def forehandToHitball(self, hitSpeed): |
程序说明
无论正手还是反手,都是机器人手臂的一系列关节角变化,通过连续的给每个关节不同的角度,即可实现击球动作。
注:最后一个关节角的速度是击球时的力度,所以其值应不同于其余值,应该给定一个较大的值。
存在的问题和不足
初级版的程序虽然整体上可以实现功能,但都是基于理想的情况下,实际测试下来,程序仍存在一些未知的bug,所以在高级版的程序中,我们要对其进行优化,主要包括以下几个方面。
逻辑思想:
- 所有的找球,看黄杆/landmark的程序必须考虑完整,即分为看到和没看到2种情况,后续的动作一定要基于之前是找到的情况。
- 当机器人一直往前走仍找不到球,或因为没看到球而认为没有球一直往前走,这种情况下该怎么办?
- 定位时没有看到球,或者看球错误导致球超出视野范围外,需要让机器人摇头找下球,重新定位。
- 击球的力度能和距离关联,实现不同的距离给定不同的力度。
- 当球的位置超过球洞,即机器人需绕道球洞后面进行击球,此时应考虑如何避开球洞。
python程序:
- 每次摇头找球或找黄杆/landmark时,第一次需要摇头,后面则不需要摇头,调用之前的值即可。
- 利用try-except对程序进行异常处理,防止实际比赛时,程序报错。
注:本博客采用的视觉算法是我师兄的一篇博客:NAO机器人高尔夫中的视觉系统设计