NAO机器人

在这里插入图片描述

摘要

本篇博客参考清华大学出版社出版的《NAO机器人程序设计》一书,对其中的部分内容进行总结,以便加深理解和记忆。

1.介绍

产品公司官网:SoftBank Robotics Group Corp

产品参考网站:NAO 6 | Aldebaran

1)仿人机器人:综合运用机械、传感器、驱动器、计算机等技术设计的一种能模仿人的形态和行为的机械电子设备,是在电子、机械及信息技术的基础上发展而来的。仿人机器人的四肢和头部能够自主完成人类所赋予的任务与命令。

2)NAO机器人介绍

①NAO是一款Aldebaran Robotics公司研制的高端仿人机器人,具有一定水平的人工智能,是世界范围、学术领域内应用最广泛的仿人机器人,是机器人世界杯RoboCup组委会指定机器人,并开放到高等教育。

②NAO本质上是一台安装了Gentoo Linux的计算机,该计算机安装了机器人专用的软硬件。主要硬件包括:2个CPU、主板、扬声器、话筒、红外线(多机器人通信控制)、2个相机、声呐(障碍物测距)、传感器(接触传感器、惯性传感器、位置传感器、压力传感器)、执行器(电机、齿轮)、语音合成器、LED灯等,运行自研的系统框架NAOqi,支持网线和wifi。


3)NAO使用方法:可在Linux、Windows、MacOS系统下用Python、C++、.NET、Java等调用NAOqi API或使用图形化的指令盒编程工具Choregraphe进行编程

4)NAO开发环境配置

5)NAO系统恢复与更新

6)项目参考文献 | 博客

地址 说明
NAO 6Aldebaran Nao6产品首页:包含Nao6产品介绍、拆箱、使用指南、初始化方案、相关软件下载等
Developer Center (aldebaran.com) 开发者中心:包含博客、课程、文档和资源
NAO - Aldebaran 2.8.7.4 documentation NAO文档,包含用户指南和开发指南
nao 6代培训手册 (book118.com) 包括,初始使用指南和可视化开发
NAO V6 开发环境配置-CSDN博客 包括环境配置和python开发脚本植入
2021软银机器人杯NAO机器人比赛培训-竞走 B站Nao竞走培训视频
yolov4目标检测并进行跟踪 基于nao机器人实现yolov4目标检测并进行跟踪

2.连接NAO

1)配置NAO的无线网络

  • 通过以太网将NAO与计算机或者交换机连接

  • 开机,听语音播报的IP地址,将本机IP设置与NAO为同一网段

  • 打开浏览器,输入NAO的IP地址,用户密码均输入nao,然后进行wifi配置

  • 将电脑连接至NAO所连的wifi

2)远程连接NAO:通过SSH远程登录,账号密码均为nao

3)NAOqi

NAOqi框架使用一致的数据模型表示信息,不同模块使用相同的编程模式,各个模块与ALMemory共享使用相同的结构

机器人启动过程中,NAOqi会自动启动,是NAO的核心依赖,脚本/etc/init.d/naoqi管理NAOqi的启动过程

# 启动NAOqi
nao start
# 停止Naoqi
nao stop
# 重启
nao restart
# 查看运行状态
nao status

3.NAOqi编程

1)NAOqi依赖

/etc/naoqi/autoload.ini(该文件会在NAOqi启动时被代理程序加载)指定了NAOqi所需的库,这些库位于/usr/lib/naoqi

2)NAOqi代理程序

在NAO机器人上执行NAOqi指令是通过一个代理程序Broker完成,使用NAOqi模块时不同于传统Python程序通过import导入模块,而是通过Broker查找对应模块并调用对应方法。

同时Broker还是一个服务器,这意味着它也可以通过网络(ip+port)而不仅是本地调用模块相应的命令

3)编程基础

①设置naoqi SDK依赖路径,后续代码都需要加入该步骤,故省略

# 注意此处应将naoqi框架的依赖路径正确添加,也可以使用其他方法导入依赖
sys.path.append(r'E:\code\Nao6\choregraphe-suite-2.8.6.23-win64-vs2015\bin')
sys.path.append(r'E:\code\Nao6\choregraphe-suite-2.8.6.23-win64-vs2015\lib')

②依赖导入和通过代理类创建模块实例

# 导入代理类,由于使用添加系统路径的方式导包,因而会报找不到的错
from naoqi import ALProxy

if __name__ == '__main__':
    # 通过代理类导入ALTextToSpeech模块,参数1:模块名称;参数2:IP,参数3:端口号
    tts = ALProxy("ALTextToSpeech", "192.168.0.47", 9559)
    # 文字转语音
    tts.say("""你好,小琳猫!""")

③阻塞调用与非阻塞调用

from naoqi import ALProxy
import time

if __name__ == '__main__':
    """非阻塞调用,边走,边说话"""
    motion = ALProxy("ALMotion", "192.168.0.47", 9559)
    tts = ALProxy("ALTextToSpeech", "192.168.0.47", 9559)
    # 设置关节的刚度(电机的转矩限制),刚度为0时关节做不了任何运动,属于非阻塞调用
    motion.setStiffnesses("Body", 1.0)
    time.sleep(1)
    # 运动进程的初始化,检测机器人的当前状态,并选择一个正确的正确的姿势,阻塞调用
    motion.moveInit()
    # 移动到指定坐标,阻塞调用。但通过post对象进行非阻塞调用
    motion.post.moveTo(1, 0.5, 0)
    # 文字转语音,阻塞调用
    tts.say("""你好,我叫李一帆""")

③脚本导入执行

可以将脚本导入到机器人的/home/nao目录下运行

4)内存模型

NAOqi的各个模块间通过内存交换数据,内存管理的模块是ALMemoryALMemory以无序映射方式存储数据,数据结构为键值对,可以通过getData("{键名}")来读取对应的值。

from naoqi import ALProxy
import time
memory = ALProxy("ALMemory","192.168.1.170",9559)
for i in range(10):
    print(memory.getData("Device/SubDeviceList/InertialSensor/AccelerometerX/Sensor/Value"))

NAOqi中的数据主要分为两类:

①NAO的状态数据:包括执行器和传感器的数据。NAOqi周期性地调用各种传感器驱动程序接口,将传感器值写入内存中

②订阅事件/微型事件数据:如人脸识别模块等处理时的运算量很大,NAOqi只有在订阅这些功能时才会向内存中写数据,订阅的模块在完成后以事件的方式通知NAOqi

5)NAOqi的数据类型

类型 Python C++
整型 int int
布尔型 bool bool
浮点型 float float
列表 vector [ ]
字符串 str std::string
二进制数据 str AL Value

6)NAO机器人状态

状态 解释
互动(Interactive) 称为自主生活ALAutonomousLife,自主生活状态下可以执行功能,同一时间只能进行一个功能,默认开启。可以通过双击机器人胸部,或在Nao网页中设置
孤立(Solitary)
保护(Safeguard)
禁用(Disabled)

4.运动控制

1)运动模型

NAO相连部件之间可以在两个甚至三个方向上做相对运动,每个方向上的运动都是通过电机驱动机械结构完成的。

①坐标系与方向:x指向身体前方,y水平从右到左,z从下向上。

NAO运动时,远离躯干的关节运动时绕轴转动,单位为弧度(注意要用角度计算时,需要进行单位转换)

②运动

机器人能够独立运动的关节数称为机器人的运动自由度(NAO为26)

腿部需要支撑NAO全身的重量

运动名称 转动幅度
HeadPitch 仰头[-38.5,29.5]抬头
HeadYaw 左转[-119.5,119.5]右转
ShoulderRoll L[-18,76]R
ShoulderPitch L[-119.5,119.5]R
ElbowYaw L[-119.5,119.5]R
WristYaw L[-104.5,104.5]R
HipYawPitch(髋关节,连接腿和躯干,控制腿内外转) 外[-65.62,42.44]内
HipPitch(髋关节,控制腿的前摆、后摆) [-88,27.73]
HipRoll(髋关节,控制腿的横向移动) [-21.74,45.29]
KneePitch(屈膝) [-5.29,121.04]
AnklePitch(脚前后转动) [-67.97,53.40]
AnkleRoll(脚左右转动) [-22.79,44.06]/[-44.06,22.79]

2)ALRobotPosture

ALRobotPosture模块可以让机器人转到不同的预定义姿势

from naoqi import ALProxy
postureProxy = ALProxy("ALRobotPosture","192.168.1.170",9559)
# 返回当前姿势名称,若当前姿势不是预定义姿势,返回unknow,阻塞调用
postureProxy.getPosture()
# 返回预定义姿势列表,阻塞调用
postureProxy.getPostureList()
# 转到预定义姿势,阻塞调用,参数1:姿势名,参数2:速度
postureProxy.goToPosture("StandInit",1.0)
postureProxy.goToPosture("SitRelax",1.0)
postureProxy.goToPosture("StandZero",1.0)
postureProxy.goToPosture("LyingBelly",1.0)
postureProxy.goToPosture("LyingBack",1.0)
postureProxy.goToPosture("Stand",1.0)
postureProxy.goToPosture("Sit",1.0)
postureProxy.goToPosture("Crouch",1.0)
# print(postureProxy.getPostureFamily)
# 转到预定义姿势,没有goToPosture的中间动作,阻塞调用
postureProxy.applyPosture("{预设动作名}",{
    
    速度})
# 停止当前动作
stopMove()

3)ALMotion

ALMotion模块包括与机器人动作相关的方法,运动任务可以阻塞调用也可以非阻塞调用

①刚度控制

设置关节的刚度相当于电机的转矩限制。

刚度为0.0时,关节位置不受电机控制,关节是自由的。刚度为1.0时,关节使用最大功率转到指定位置。刚度∈(0.0,1.0)时,如果关节移动到目标位置所需的转矩高于刚度的限制,关节不会达到目标位置。

可以通过肢体名称控制一组关节的刚度,也可以单独设置某一关节的刚度

from naoqi import ALProxy

motionProxy = ALProxy("ALMotion","192.168.1.170",9559)
# 唤醒机器人,启动电机,刚度均为1
motionProxy.wakeUp()
# 获取机器人的唤醒状态
motionProxy.robotIsWakeUp()
# 机器人休息,关闭电机,刚度均为0
motionProxy.rest()
# 设置刚度,非阻塞调用
motionProxy.setStiffness({
    
    关节名s},{
    
    刚度})
# 获取刚度
motionProxy.getStiffness({
    
    关节名s})
# 按时间序列设置刚度,阻塞调用
motionProxy.stiffnessInterpolaition({
    
    关节名s},{
    
    刚度},{
    
    时间序列})
# 分别在1.2.3.4秒将HeadYaw的刚度设置为0.5,0.5,1.0,0.0
motionProxy.stiffnessInterpolation(['HeadYaw'],[0.25,0.5,1.0,0.0],[1.0,2.0,3.0,4.0])

②关节控制

关节控制用于精确控制机器人的关节位置,可以控制一个关节或同时控制多个关节。肢体通过关节从一个位置转到另一个位置起始速度和终止速度为0,转动的角度是非线性的。

关节控制通常要经历多个ALMotion周期(20ms),为使关节平稳转动,每个一个周期会重新计算电机电流和刚度变化

from naoqi import ALProxy
import almath
motionProxy = ALProxy("ALMotion","192.168.1.170",9559)

# 插值运动,阻塞调用,类似于动画,在起始位置和终止位置之间插入若干动作
# 参数1:关节列表,参数2:角度列表:参数3:时间列表,参数4:是否为绝对角度
motionProxy.angleInterpolation(["HeadYaw","HeadPitch"],[30 * almath.TO_RAD,30 * almath.TO_RAD],[1.0,2.0],true)
# 带速度限制的插值运动,阻塞调用
# 参数1:关节列表,参数2:角度列表,参数3:最大速度比
motionProxy.angleInterpolationWithSpeed(names,targetAngles,maxSpeedFraction)
# 贝塞尔角度插值,使用贝塞尔曲线(在起始和终止时间内使用平滑曲线控制,使关节运动过程更加平稳)
# 参数1:关节列表,参数2:时间列表,参数3:控制点列表
motionProxy.angleInterpolationBezier(names,times,controlPoints)

# 反应式控制,非阻塞调用,适合于反复控制场景(相互矛盾的命令序列),以保证运动平滑且连续
# 参数1:关节列表,参数2:角度列表,参数3:最大速度比
motionProxy.setAngles(names,angles,maxSpeedFraction)
# 参数1:关节列表,参数2:角度列表,参数3:最大速度比
motionProxy.changeAngles(names,angles,maxSpeedFraction)
# 获取关节角度
# 参数1:关节列表,参数2:是否返回传感器角度,true为传感器角度,false为执行器角度
motionProxy.getAngles(names,useSensors)
# 合上双掌,阻塞调用
motionProxy.closeHand(LHand/RHand)
# 打开双掌,阻塞带哦用
motionProxy,openHand(LHand/RHand)

关节运动与身体平衡:当关节运动时,机器人重心会发生变化,严重时会摔倒。为保持身体平衡,需要同时改变多个关节的角度并调节关节运动的速度。

③运动控制

运动控制用于控制机器人的行走,包括指定位置、速度、目标等行走方式。NAO的行走控制使用“线性倒立摆”模型,行走控制的目标是尽快达到一个平衡位置,且没有大的振荡和过大的角度和速度。

每步包括双腿支撑单腿支撑两个阶段,其中双腿支撑占1/3。行走初始阶段和结束阶段双腿支撑时间为0.6s。脚运行的轨迹是一条平滑曲线,使用SE3插值计算出来。脚步沿该曲线运动既符合速度又能保持平稳。不管做哪种行走控制,都需要使用步态规划,指定步长、步频率、最大高度等。

步态参数

参数名 解释 默认值 范围 可修改
MaxStepX 沿X方向的最大前移距离 4cm [1,8]cm
MinStepX 沿X方向的最小前移距离 -4cm
MaxStepY 沿Y轴的最大平移绝对值 14cm [10.1,16]cm
MinStepTheta 沿Z轴旋转角度最大绝对值 0.349° [0.001,0.524]°
MaxStepFrequency 最大步频 1 [0,1]
MinStepPeriod 最小步周期 0.42
MaxStepPeriod 最大步周期 0.6
StepHeight Z方向抬脚最大高度 2cm [0.5,4]cm
TorsoWx 躯干与X轴间最大角度 [-0.122,0.122]°
TorsoWy 躯干与Y轴间最大角度 [-0.122,0.122]°
FootSeparation Y方向两脚之间的距离 10cm
MinFootSeparation Y方向两脚之间的最小距离 8.8cm
from naoqi import ALProxy
import almath
motionProxy = ALProxy("ALMotion","192.168.1.170",9559)
# 设置步态,非阻塞调用
# 参数1:腿名LLeg/RLeg,参数2:[x,y,theta],参数3:时间列表,参数4:True清楚已有参数
motionProxy.setFootSteps(legName,footSteps,timeList,clearExisting)
# 设置步态,阻塞调用,带速度
motionProxy.setFootStepsWithSpeed(legName,footSteps,fractionMaxSpeed,clearExisting)
# 获取实际的步态,非阻塞调用
motionProxy.getFootSteps()
# 初始化运动进程,阻塞调用
motionProxy.moveInte()
# 是否运动,运动返回true,阻塞调用
motionProxy.moveIsActive()
# 等待,直到行走任务完成。用于阻塞程序向下运行直到行走任务结束
motionProxy.waitUntilMoveIsFinished()
# 获取步态参数,Max,Min,Default,阻塞调用
motionProxy.getMoveConfig("Max")
# 获取机器人位置,阻塞调用,True返回传感器值,阻塞调用
motionProxy.getRobotPosition(True)
# 获取机器人速度,返回值为x、y、z方向的速度,阻塞调用
motionProxy.getRobotVelocity()
# 设置运动过程中手臂是否可动,True可动,参数1、2分别为左右手臂,阻塞调用
motionProxy.setArmsEnabled(leftArmEnable,rightArmEnable)
# 获取运动过程中手臂可动,阻塞调用
getMoveArmsEnabled(LArm/RArm/Arms)
# 运动,阻塞调用
motionProxy.post.moveTo(1.0,0.0,0.0)

5.视觉模块

NAO头部有两台相机:前额相机,ID为0,主要用于拍摄远景图像;嘴部相机,ID为1,主要用于拍摄下方图像。其最大帧率为30,最大分辨率为1280×690。

1)NAO支持的色彩空间与分辨率

NAO如果按照传统的RGB颜色空间的话每帧图像会占用很大的空间,他支持多种图像压缩算法,可以将图像保存为不同格式。

2)ALPhotoCapture

ALPhoteCapture模块主要用于拍摄图像(组)并保存

photoProxy = ALProxy("ALPhotoCapture","192.168.1.170",9559)
# 获取相机ID
photoProxy.getCameraID()
# 设置拍照相机,默认为上部相机
photoProxy.setCameraId(0/1)
# 设置拍照时间间隔,参数为整数,单位为ms,默认值为200
photoProxy.setCaptureInterval(200)
# 获取拍照时间间隔
photoProxy.getCaptureInterval()
# 设置颜色空间,参考上表ID
photoProxy.setColorSpace(colorSpace)
# 设置保存图像的格式,bmp、jpg、png、tiff等
photoProxy.setPictureFormat("{图像格式}")
# 拍摄图像
photoProxy.takePicture("{保存路径}","{文件名}",overwrite=False)
# 拍摄图像组
photoProxy.takePictures({
    
    拍摄图像数},"{保存路径}","{文件名}"[,overwrite=False])

3)ALVideoRecorder

ALVideoRecorder模块用于录制视频,保存为avi格式

videoRecorderProxy = ALProxy("ALVideoRecorder","192.168.1.170",9559)
# 设置拍照相机,默认为上部相机
videoRecorderProxy.setCameralID(0/1)
# 获取拍照相机ID
videoRecorderProxy.getCameralID()
# 设置帧率,最大为30,VGA下最大为15
videoRecorderProxy.setFrameRate(frameRate)
# 获取帧率
videoRecorderProxy.getFrameRate()
# 设置颜色空间,上表中的ID值
videoRecorderProxy.setColorSpace({
    
    })
# 设置视频格式,"IYUV"或"MJPG"
videoRecorderProxy.setVideoFormat({
    
    })
# 设置分辨率,参照上表ID
videoRecorderProxy.setResolution({
    
    })
# 开始摄像,非阻塞调用,调用后开启新线程录制视频
videoRecorderProxy.startRecording("{保存路径}","{保存文件名}"[,overwrite=False])
# 当前是否录制
videoRecorderProxy.isRecording()
# 结束拍摄
videoRecorderProxy.stopRecording()

4)ALVideoDevice

ALVideoDevice负责从相机获取的源图像进行前期处理(将YUV图像转换为其他色彩空间图像、向ARV视频文件添加时间戳、封装图像数据等)

①NAO机器人相机的图像传感器为MT9M114,相机驱动程序输出YUV422图像。视觉模块订阅ALVideoDevice模块,并将YUV图像数据流转换为所需格式,若视觉模块需要的就是YUV模式,则可以直接访问原始数据。其不同格式数据流的处理时间依次为:YUV422 < Yuv < YUV < RGB/BGR < HSY。

NAO的CPU处理能力有限,在网络带宽较低时,远程传送每秒实际的最大帧数会小于30,且分辨率越大,帧数越少。

②图像封装数据格式

# ALImage 列表
# 整数,图像宽度;整数,图像高度;整数,图像层数,像素字节数;整数,色彩空间;整数,时间戳s;整数,时间戳ms;整数,图像数据;整数,相机ID;浮点数,相机左视角(弧度),FOV;浮点数,相机上视角;浮点数,相机右视角;浮点数,相机下视角
image[0],image[1],image[2],image[3],image[4],image[5],image[6],image[7],image[8],image[9],image[10],image[11]

③订阅图像

ALVideoDevice模块是视觉模块中最复杂,方法最多的模块,包括一些弃用的方法在内有100多种API,可以分为订阅管理、相机管理、单数据流管理和多数据流管理等,以下仅给出订阅图像的示例:

流程:订阅ALVideoDevice模块 → 获取图像 → 释放图像 → 解除订阅

videoDeviceProxy = ALProxy("ALVideoDevice","192.168.1.170",9559)
# 设置相机
videoDeviceProxy.setActiveCamera(0/1)
# 获取当前使用的相机
videoDeviceProxy.getActiveCamera()
# 订阅拍摄,图像存储在缓冲区,返回订阅的句柄
videoClient = videoDeviceProxy.subscribeCameras("{订阅模块名,任意}",{
    
    相机ID},{
    
    分辨率},{
    
    色彩空间},{
    
    帧率})
# 远程获取拍摄后的图像,返回的是ALImage实例
image = videoDeviceProxy.getImageRemote(videoClient)
# 本地获取拍摄后的图像,返回的是ALImage实例
image = videoDeviceProxy.getImageLocal(videoClient)
# 释放图像缓存
videoDeviceProxy.releaseImage(videoClient)
# 解除订阅
videoDeviceProxy.unsubscribe(videoClient)
# 打开并初始化相机
videoDeviceProxy.openCamera({
    
    相机ID})
# 关闭相机并释放资源
videoDeviceProxy.closeCamera({
    
    相机ID})

5)视频检测

视频检测包括红球检测ALRedBallDetection,标识检测ALLandMarkDetection,运动检测ALMovementDetection,检测并读取二维码ALBarcodeReader,人脸检测ALFaceDetection,黑光检测ALBlacklightingDetection,黑暗检测ALDarknessDetection,3D分割检测ALSegmentation3D,视觉罗盘ALVisualCompass等十余种。

这些模块都继承于ALVisionExtractor模块,其整个继承关系如下图:

6)视频识别

NAO能够识别事先学习过的Object,对应模块为视频识别ALVisionRecognition,其识别过程为:

①学习过程:通过Choregraphe抽取识别对象的关键特征并存储(标定学习对象、特征提取、指定对象名称和方位、存储、将数据库导入到机器人中)

②识别过程

③数据结构

6.音频模块

7.传感器

8.使用C++编程

9.Choregraphe可视化开发

猜你喜欢

转载自blog.csdn.net/qq_44930244/article/details/130103747