目录
一、关节控制方法
关节控制通常要经历多个ALMotion周期(20ms)。为使关节平稳转动,每隔20ms,API需要重新计算电机电流和刚度变化。
控制关节或关节组有两种方式:
(1)插值方法,阻塞调用,类似于动画,在起始位置和终止位置间定时插入若干中间值。
(2)反应式方法,非阻塞调用,通常在反应控制中多次调用。例如在头部跟踪时,NAO可能得到一组相互矛盾的命令序列(如前一个命令是头部左转,后一个命令是右转),调用反应式方法可以保证运动平滑且速度连续。
方法名 | 说明 | 调用方式 |
angleInterpolation (names, angleLists, timeLists, isAbsolute) | 插值运动。names 是关节名称;angleLists是角度、角度列表或二维角度列表,单位为弧度;timeLists是为达到目标角度的时间、时间列表或二维时间列表;isAbsolute为True,代表绝对角度,为False,代表相对角度。 | 阻塞调用 |
angleInterpolationWithSpeed(names, targetAngles, maxSpeedFraction) | 插值运动(带速度限制)。names是关节名称;targetAngles为弧度表示的角度或角度列表,maxSpeedFraction为最大速度百分比 | 阻塞调用 |
angleInterpolationBezier(jointNames, times,controlPoints) | 贝塞尔角度插值。jointNames为关节名称列表,times为时间列表,controlPoints为控制点列表 | 阻塞调用 |
setAngles(names,angles, fractionMaxSpeed) | 设置关节角度。names是关节名称;angles为一个或多个角度;maxSpeedFraction为最大速度百分比 | 非阻塞调用 |
changeAngles(names,angles, fractionMaxSpeed) | 改变关节角度。names是关节名称;angles为一个或多个角度;maxSpeedFraction为最大速度百分比 | 非阻塞调用 |
getAngles(names, useSensors) | 获取关节角度。names为关节名;useSensors为True返回关节传感器角度,为False返回执行器角度 | |
closeHand(handName) | 合上手掌。handName取值:LHand,RHand | 阻塞调用 |
openHand(handName) | 张开手掌。handName取值:LHand,RHand | 阻塞调用 |
1.1 控制关节
需要给出关节名称、以弧度为单位的目标角度和转到目标角度的速度。
almath是NAOqi系统提供的数学函数库,almath.TO_RAD为1度所对应的弧度数,在关节控制方法中,目标角度都以弧度为单位,本例及以后示例中,目标角度都用角度*1度对应弧度数形式表示。 setAngles()方法为非阻塞调用方法,本例中转头动作后面的语句将头部刚度设置为0,因此调用setAngles()方法时需要使用延时。
# 头部关节运动(头部左转30度)
import time
import almath
class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self)
self.motion=ALProxy("ALMotion")
def onLoad(self):
pass
def onUnload(self):
pass
def onInput_onStart(self):
self.motion.setStiffnesses("Head", 1.0)
names = "HeadYaw"
angles = 30.0*almath.TO_RAD
fractionMaxSpeed = 0.1 # HeadYaw joint at 10% max speed
self.motion.setAngles(names,angles,fractionMaxSpeed)
time.sleep(3.0)
self.motion.setStiffnesses("Head", 0.0)
pass
def onInput_onStop(self):
self.onUnload()
self.onStopped()
1.2 定时插值
如果关节运动的轨迹是已知的,angleInterpolation()[阻塞调用]和angleInterpolationWithSpeed()方法可以在每个ALMotion周期中重新计算执行器(电机)参数,控制关节运动速度,使机器人运动平稳。关节插值运动方法可以对一个关节或多个关节指定运动角度,也可以指定角度序列及完成这些动作的时间序列。
# 头部插值运动
import almath
import time
class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self)
self.motion=ALProxy("ALMotion")
def onLoad(self):
pass
def onUnload(self):
pass
def onInput_onStart(self):
self.motion.setStiffnesses("Head", 1.0)
names = "HeadYaw"
angleLists = 50.0*almath.TO_RAD
timeLists = 1.0
isAbsolute = True
self.motion.angleInterpolation(names, angleLists, timeLists, isAbsolute)
time.sleep(1.0)
names= "HeadYaw"
angleLists = [30.0*almath.TO_RAD, 0.0]
timeLists = [1.0, 2.0]
isAbsolute = True
self.motion.angleInterpolation(names, angleLists, timeLists, isAbsolute)
time.sleep(1.0)
names = ["HeadYaw", "HeadPitch"]
angleLists = [30.0*almath.TO_RAD, 30.0*almath.TO_RAD]
timeLists = [1.0, 1.2]
isAbsolute = True
self.motion.angleInterpolation(names, angleLists, timeLists, isAbsolute)
names = ["HeadYaw","HeadPitch"]
angleLists=[[50.0*almath.TO_RAD, 0.0],[-30.0*almath.TO_RAD, 30.0*almath.TO_RAD, 0.0]]
timeLists=[[1.0, 2.0], [ 1.0, 2.0, 3.0]]
isAbsolute=True
self.motion.angleInterpolation(names, angleLists, timeLists, isAbsolute)
self.motion.setStiffnesses("Head", 0.0)
pass
def onInput_onStop(self):
self.onUnload()
self.onStopped()
1.3 反应控制
setAngles() 和changeAngles()方法为非阻塞调用方法,经常用于关节反应式控制,在前一个调用未完成前,可以执行下一个调用。
# 反应控制
import time
class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self)
self.motion=ALProxy("ALMotion")
def onLoad(self):
pass
def onUnload(self):
pass
def onInput_onStart(self):
self.motion.setStiffnesses("Head", 1.0)
names = "HeadYaw"
angles = 0.3
fractionMaxSpeed = 0.1
self.motion.setAngles(names,angles,fractionMaxSpeed)
time.sleep(0.5) # wait half a second
angles = 0.0 # change target
self.motion.setAngles(names,angles,fractionMaxSpeed)
time.sleep(0.5) # wait half a second
angles = 0.1 # change target
self.motion.setAngles(names,angles,fractionMaxSpeed)
time.sleep(3.0)
self.motion.setStiffnesses("Head", 0.0)
pass
def onInput_onStop(self):
self.onUnload()
self.onStopped()
1.4 读关节角度
关节控制是通过调用API方法,将关节控制命令发送给相应的执行器(电机)完成的。命令执行完成后,关节角度应该与发给执行器的命令相一致(实际的机械结构执行过程中可能存在微小误差)。NAO在每个关节上安装了位置传感器测量关节角度。getAngles()方法即可以读取执行器角度,也可以读取传感器角度。
# 获取关节角度
import time
class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self)
self.motion=ALProxy("ALMotion")
def onLoad(self):
pass
def onUnload(self):
pass
def onInput_onStop(self):
self.onUnload()
self.onStopped()
def onInput_onStart(self):
self.motion.setStiffnesses("Head", 1.0)
names = "HeadYaw"
angles = 0.3
fractionMaxSpeed = 0.8
self.motion.setAngles(names,angles,fractionMaxSpeed)
time.sleep(1.5)
names = "Head" #包括HeadYaw和HeadPitch
useSensors = False
commandAngles = self.motion.getAngles(names, useSensors) #读取执行器角度
self.logger.info("Command angles:")
self.logger.info(str(commandAngles))
useSensors = True
sensorAngles = self.motion.getAngles(names, useSensors) #读取传感器角度
self.logger.info("Sensor angles:")
self.logger.info(str(sensorAngles))
errors = []
for i in range(0, len(commandAngles)):
errors.append(commandAngles[i]-sensorAngles[i])
self.logger.info("Errors")
self.logger.info(errors)
pass