Skip to main content

机械臂运动控制实验

教学目的(教师视角)

学生通过阅读本节材料,从中提炼出有用的信息,完成机械臂运动控制的“示教拖动”的机器人实验。通过本节的学习,学生将能够:

  1. 通过“研究的方式”自行学习,从而培养独立、自主学习能力。
  2. 增强面对陌生问题时的解决问题能力,从而提升解决问题的自信心。
  3. 通过“实践的方式”完成机械臂运动控制的“示教拖动”的机器人实验,从而培养动手能力。

课程目的(学生视角)

本节课的目标只有一个:让机械臂能够动起来。具体来说:

以目标为导向
  1. 让机械臂用“示教拖动”的方式完成一个简单的机械臂运动控制的机器人实验。参见 “拖动示教”
  2. 为了完成上述任务,我们需要了解如何在机械臂树莓派上运行 Python 代码。参见 “熟悉开发环境”
  3. 如果好奇心足够强烈,还可以了解一下机械臂树莓派的电路控制的接口。参见 附录

当然在本实验中,不必了解这么底层的细节。但是如果有需要,这也是一个学习底层原理的契机。

拖动示教

01. 熟悉开发环境

机械臂运动控制实验的准备工作

1.1 机器人系统介绍

  • 系统简介

    • Ubuntu 是个人桌面操作系统中使用最广泛的 linux 操作系统。对于初学者来说,熟悉 linux 环境或一些嵌入式硬件操作系统是一个很好的选择。ubuntu 官方网站还发布了树莓派专用操作系统。

      2.1.5.4-20.04System-001

  • 系统功能简介

    • myStudio :固件烧录软件,用于更新和烧录新版本固件
    • myBlockly :图形化编程软件,可直接通过拖拽积木组成运行代码,控制机械臂
    • ROS1 Shell :直接进入编译好的 ROS1 环境,可直接输入对应指令,运行对应的 ROS1 代
    • ROS2 Shell :直接进入编译好的 ROS2 环境,可直接输入对应指令,运行对应的可直接运行 ROS2 代码
    • Github-ElephantRobotics :大象机器人官方开源代码仓库
    • Home-ElephantRobotics :大象机器人官网主页
    • UserManual - CN/EN :机器使用手册,包含所有关于机械臂控制的内容
    • WiFi_ON/OFF :WiFi 开关,点击即可打开/关闭 WiFi 功能
    • HotSpot_ON/OFF :热点开关,点击即可打开/关闭热点功能,打开后热点名称为 ElephantRobotics_AP_XXXX
    • Language Support :系统语言设置,点击即可进入系统语言设置界面

1.2 系统密码说明

  • 开机账户密码 & VNC 连接密码 & SSH 连接密码 & 管理员账户密码
    • 统一为: Elephant
  • 如何定义新的密码 (我们学生不要改密码)
    • 更改账户密码
      • 使用快捷键 ctrl + alt + T 打开终端
      • 输入 passwd 进行账户密码修改
      • 输入新密码两次即可
    • 更改 VNC 连接密码
      • 使用快捷键 ctrl + alt + T 打开终端
      • 输入 vncpasswd 进行账户密码修改
      • 输入新密码两次即可
    • 更改 SSH 连接密码
      • SSH 远程连接输入的就是管理员账户密码,无需单独修改
    • 更改管理员账户密码
      • 使用快捷键 ctrl + alt + T 打开终端
      • 输入 sudo passwd 进行账户密码修改
      • 输入新密码两次即可

1.3 VNC

  • VNC 功能介绍

    • 是一款远程控制的软件,一般用于远程解决电脑故障或软件调试
  • VNC 端口说明

    • 机械臂与 PC 连接在同一 WiFi 下,机械臂 IP 地址即为端口
  • 连接 VNC。连接的方式有两种,

    1. 第一种方式需要外接显示器对系统进行一些操作,具体步骤如下:

      先点击 "Disconnect" 关闭默认热点

      点击 "Enable Wi-Fi" ,等待显示当前可用的 WiFi

      单击需要连接的 WiFi,输入 WiFi 密码

      连接成功后,点击 "Connection Information" ,查询机械臂当前 IP 地址

      如示例所示, "192.168.10.64" 即为机械臂当前 IP 地址

      将您的电脑和机械臂的 WiFi 连接到同一个 WiFi 下,打开 VNC viewer 软件,输入这个 IP 地址(以上方的案例举例则为输入 192.168.10.64 )然后回车,密码是 Elephant,用户名默认是不填的,成功连接示例如下:

    2. 第二种方式不需要连接显示屏,直接用 PC 连接 Ubuntu 系统热点进行远程控制,但是此连接方式并不具备网络冲浪的功能,只能远程操控机械臂系统,具体步骤如下:

      PC 选择连接 Ubuntu 系统热点 ElephantRobotics_AP_XXXX ,输入密码 Elephant

      打开 VNC viewer 软件,输入这个 IP 地址 10.42.0.1 ,然后回车,密码是 Elephant,用户名默认是不填的,成功连接示例如下:

  • 如何提高流畅性

    • 远程连接流畅性取决于所连 WiFi 的流畅性,建议连接稳定的 WiFi 进行远程控制

1.4 SSH

  • SSH 功能介绍

    • 简单说,SSH 是一种网络协议,用于计算机之间的加密登录。如果一个用户从本地计算机,使用 SSH 协议登录另一台远程计算机,我们就可以认为,这种登录是安全的,即使被中途截获,密码也不会泄露。
  • SSH 端口说明

    • 默认端口 22,无需更改
  • SSH 首次连接

    1. 按照 2.3 VNC 确认机械臂 IP 地址

    2. 在个人电脑按下 win + R 打开运行界面,在输入框输入 cmd

    1. 输入完毕后点击确认,即可打开 shell 界面

    1. 输入 ssh er@IP地址 ,然后回车(IP 地址以机械臂显示为主,图中仅为示例)

    1. 输入密码 Elephant

    1. 如上图所示,已成功远程 ssh 连接机械臂
  • 如何提高流畅性

    • 远程连接流畅性取决于所连 WiFi 的流畅性,建议连接稳定的 WiFi 进行远程控制

1.5 网络配置

  • 默认 AP 的使用

    • 在机械臂开机后,系统会默认连接到树莓派自身发出的热点,热点名称为 ElephantRobotics_AP_XXXX ,此时 IP 地址为 10.42.0.1 ,此热点并不具备网络冲浪的功能,并且传输的速率与信息量有限,所以在最终成像时会有部分的失真和色差,并且通信传输也会有延迟,属于正常现象。
  • 连接 WLAN

    先点击 "Disconnect" 关闭默认热点

    点击 "Enable Wi-Fi" ,等待显示当前可用的 WiFi

    单击需要连接的 WiFi,输入 WiFi 密码

    连接成功后,点击 "Connection Information" ,查询机械臂当前 IP 地址

    如示例所示, "192.168.10.64" 即为机械臂当前 IP 地址

  • 连接有线网络

    机械臂打开后,默认连接到系统配置的热点:ElephantRobotics_AP_XXXX

    点击 "Disconnect" ,断开默认热点连接

    将网线连接到机械臂的网络端口

    将普通互联网网线连接到机械臂的网络端口

  • 如何设定默认 IP 地址

    使用快捷键 ctrl + alt + T 打开终端,输入 sudo vim /etc/netplan/01-network-manager-all.yaml ,修改 01-network-manager-all.yaml 文件为如下内容

    修改完成后,输入 sudo netplan apply 使配置生效

  • 如何自动分配 IP 地址

    系统连上 WiFi 就是自动分配 IP 地址,无需进行任何设置,如果想从固定 IP 地址改成自动分配 IP 地址,修改/etc/netplan/01-network-manager-all.yaml 文件为如下内容即可

    修改完成后,输入 sudo netplan apply 使配置生效

1.6 蓝牙配置

  • 机械臂系统蓝牙默认打开,直接用 PC/手机,打开蓝牙搜索即可搜索到,如图所示,蓝牙默认名称为 MyCobot-Pi

  • PC/手机向机械臂传输文件

    • 选择想要进行蓝牙传输的文件,使用蓝牙传输

    • 在机械臂系统内操作,选择接收文件

    • 等待蓝牙传输完成

    • /home/er/Downloads 文件夹即可看到蓝牙传输完成的文件

  • 机械臂系统向 PC/手机传输文件

    • 系统内点击蓝牙图标,在下拉显示框中选择 Send Files to Device

    • 选择 PC/手机

    • 在 PC/手机上,允许接收文件即可实现从机械臂向手机传输信息

1.7 语言配置

  • 如何切换语言

    点击桌面 Language Support 进入语言切换界面,拖拽想要更换的语言到最上方,重启系统即可

  • 如何下载语言

    点击桌面 Language Support 进入语言切换界面,选择语言,点击下载,输入密码 Elephant

1.8 系统分辨率切换

  • 点击屏幕右上角图标,选择 System Settings ,进入系统控制面板

  • 选择 Display ,进入分辨率选择页面

  • 切换选择分辨率,点击 Apply 查看显示效果,如果符合,则点击 Keep this Configuration

1.9 Python

  • 机器人系统 python 简介

    系统内置安装 Python3.8 ,无需自行安装

    已按照 Python 依赖:

PackageVersion
pymycobot3.0.9
pyserial3.5
numpy1.23.5
opencv-contrib-python4.7.0.72
rospkg1.4.0
rospkg-modules1.4.0
  • 初次使用 python

    在终端中输入

    python3

    即可进入 python 的编译环境。

    出现 >>> 标志后即代表进入了 python 的环境。

    可在输入框中尝试此代码

    print ("Hello World!")

    此时终端会反馈回 Hello World 字样

    可以在终端输入 pip list 来查看目前已经有的 python 的包

  • 运行机器人案例代码

    具体案例代码可查看 Python 章节,直接将案例中代码复制下来即可使用

02. 机械臂运动控制:实现拖动示教

2.1 适用设备

  • myCobot 280 Pi
  • myCobot 320 Pi
  • mechArm 270 Pi
  • myArm 300 Pi
  • myPalletizer 260 Pi

2.2 操作步骤

Step 1: Atom 烧录最新版的 atomMain

Step 2: 新建一个 Python 文件为 .py ,将下列代码拷贝进去并保存。

可以使用 nano 命令在 Ubuntu 系统中来创立 python 文件。首先打开终端(同时按下 CTRL+ALT+T),输入

nano drag_trial_teaching.py

然后按下回车键,即可创建名为 drag_trial_teaching 的 python 文件。此处的 drag_trial_teaching 为创立的 python 文件的文件名,您可以在此更改为任意名称。按下回车键后会进入代码编辑页面,您可以在此界面对进行 python 程序代码编写。可以把拖动示教的代码拷贝至此处。(图中的文件名为 demo2)

当编写好 python 程序后,按下 ctrl+s 来对已经编辑好的程序进行保存,再按下 ctrl+x 退出编辑器。

注意: 文件命名为:drag_trial_teaching.py。

Github 下载地址

import time
import os
import sys
import termios
import tty
import threading
import json
import serial
import serial.tools.list_ports

from pymycobot.mycobot280 import MyCobot280
from pymycobot.mycobot320 import MyCobot320
from pymycobot.mecharm270 import MechArm270
from pymycobot.myarm import MyArm
from pymycobot.mypalletizer260 import MyPalletizer260

port: str
mc: MyCobot280
sp: int = 80

def setup():
global port, mc

print("")

print("1 - MyCobot280")
print("2 - MyCobot320")
print("3 - MechArm270")
print("4 - MyArm300")
print("5 - MyPalletizer260")
_in = input("Please input 1 - 5 to choose:")
robot_model = None
if _in == "1":
robot_model = MyCobot280
print("MyCobot280\n")
print("Please enter the model type:")
print("1. Pi")
print("2. Jetson Nano")
print("Default is Pi")
model_type = input()

if model_type == "2":
port = "/dev/ttyTHS1"
else:
pass
elif _in == "2":
robot_model = MyCobot320
print("MyCobot320\n")

elif _in == "3":
robot_model = MechArm270
print("MechArm270\n")

elif _in == "4":
robot_model = MyArm
print("MyArm300\n")

elif _in == '5':
robot_model = MyPalletizer260
print('MyPalletizer260\n')

else:
print("Please choose from 1 - 5.")
print("Exiting...")
exit()

plist = list(serial.tools.list_ports.comports())
idx = 1
for port in plist:
print("{} : {}".format(idx, port))
idx += 1

if not plist:
pass
else:
_in = input("\nPlease input 1 - {} to choice:".format(idx - 1))
port = str(plist[int(_in) - 1]).split(" - ")[0].strip()
print(port)
print("")

baud = 1000000
_baud = input("Please input baud(default:1000000):")
try:
baud = int(_baud)
except Exception:
pass
print(baud)
print("")

DEBUG = False
f = input("Wether DEBUG mode[Y/n](default:n):")
if f in ["y", "Y", "yes", "Yes"]:
DEBUG = True
mc = robot_model(port, baud, debug=DEBUG)
mc.power_on()

class Raw(object):
"""Set raw input mode for device"""

def __init__(self, stream):
self.stream = stream
self.fd = self.stream.fileno()

def __enter__(self):
self.original_stty = termios.tcgetattr(self.stream)
tty.setcbreak(self.stream)

def __exit__(self, type, value, traceback):
termios.tcsetattr(self.stream, termios.TCSANOW, self.original_stty)

class Helper(object):
def __init__(self) -> None:
self.w, self.h = os.get_terminal_size()

def echo(self, msg):
print("\r{}".format(" " * self.w), end="")
print("\r{}".format(msg), end="")

class TeachingTest(Helper):
def __init__(self, mycobot) -> None:
super().__init__()
self.mc = mycobot
self.recording = False
self.playing = False
self.record_list = []
self.record_t = None
self.play_t = None
self.path = os.path.dirname(os.path.abspath(__file__)) + "/record.txt"

def record(self):
self.record_list = []
self.recording = True

# self.mc.set_fresh_mode(0)
if isinstance(self.mc, MyCobot320):
self.mc.release_all_servos(1)
else:
self.mc.release_all_servos()

def _record():
while self.recording:
start_t = time.time()
if isinstance(self.mc, (MyArm, MyPalletizer260)):
angles = self.mc.get_encoders()
interval_time = time.time() - start_t
if angles:
record = [angles, interval_time]
self.record_list.append(record)
print("\r {}".format(time.time() - start_t), end="")
else:
angles = self.mc.get_encoders()
speeds = self.mc.get_servo_speeds()
interval_time = time.time() - start_t
if angles and speeds:
record = [angles, speeds, interval_time]
self.record_list.append(record)
print("\r {}".format(time.time() - start_t), end="")

self.echo("Start recording.")
self.record_t = threading.Thread(target=_record, daemon=True)
self.record_t.start()

def stop_record(self):

if isinstance(self.mc, MyArm):
self.mc.power_on()
else:
self.mc.focus_all_servos()

if self.recording:
self.recording = False
self.record_t.join()
self.echo("Stop record")

def play(self):
self.echo("Start play")
if isinstance(self.mc, (MyArm, MyPalletizer260)):
for record in self.record_list:
encoders, interval_time = record
self.mc.set_encoders(encoders, 100)
time.sleep(interval_time)
else:
i = 0
for record in self.record_list:
encoders, speeds, interval_time = record
self.mc.set_encoders_drag(encoders, speeds)
if i == 0:
time.sleep(3)
i += 1
time.sleep(interval_time)
self.echo("Finish play")

def loop_play(self):
self.playing = True

def _loop():
while self.playing:
self.play()

self.echo("Start loop play.")
self.play_t = threading.Thread(target=_loop, daemon=True)
self.play_t.start()

def stop_loop_play(self):
if self.playing:
self.playing = False
self.play_t.join()
self.echo("Stop loop play.")

def save_to_local(self):
if not self.record_list:
self.echo("No data should save.")
return
with open(self.path, "w") as f:
json.dump(self.record_list, f, indent=2)
self.echo("save dir: {}\n".format(self.path))

def load_from_local(self):
with open(self.path, "r") as f:
try:
data = json.load(f)
self.record_list = data
self.echo("Load data success.")
except Exception:
self.echo("Error: invalid data.")

def print_menu(self):
print(
"""\
\r q: quit
\r r: start record
\r c: stop record
\r p: play once
\r P: loop play / stop loop play
\r s: save to local
\r l: load from local
\r f: release mycobot
\r----------------------------------
"""
)

def start(self):
self.print_menu()

while not False:
with Raw(sys.stdin):
key = sys.stdin.read(1)
if key == "q":
break
elif key == "r": # recorder
self.record()
elif key == "c": # stop recorder
self.stop_record()
elif key == "p": # play
if not self.playing:
self.play()
else:
print("Already playing. Please wait till current play stop or stop loop play.")
elif key == "P": # loop play
if not self.playing:
self.loop_play()
else:
self.stop_loop_play()
elif key == "s": # save to local
self.save_to_local()
elif key == "l": # load from local
self.load_from_local()
elif key == "f": # free move
if isinstance(self.mc, MyCobot320):
self.mc.release_all_servos(1)
else:
self.mc.release_all_servos()
self.echo("Released")
else:
print(key)
continue

if __name__ == "__main__":
setup()
recorder = TeachingTest(mc)
recorder.start()

Step 3: 打开终端(快捷键 CTRL+ALT+t),输入以下命令:

python3 drag_trial_teaching.py

Step 4: 选择机型序号 Please input 1 - 5 to choose:,这里输入 4 1,回车。

Step 5: 点击回车,会出现指令 Please input 1 - 1 to choice:,输入 1,回车。

Step 6: 输入相对应的波特率,按下回车键。

  • myCobot 280-Pi:1000000
  • myCobot 320-Pi:115200
  • myCobot 270-Pi:1000000
  • myArm 300-Pi:115200
  • myPalletizer 260-Pi: 1000000

Step 7: 是否查看 BAUD,输入 Y/N,按下回车键。

Step 8: 键盘输入 r ,开始录制,此时可以开始拖拽机械臂。

  • 键盘输入 q,退出此程序

这里输入了 r

Step 9: 键盘输入 c,停止录制

Step 10: 键盘输入 p(小写字母),播放一次。

输入 P(大写字母),循环播放 / 停止循环播放

输入 q 终止循环,退出程序

输入 f,释放机械臂各个关节(可用于机械臂停止运动,各个关节锁定时使用)

03. 附录:通用硬件接口说明

3.1 40PIN GPIO

  • 简介

    • GPIO 全称:通用型输入输出端口(General-purpose input/output)
    • 目前我司所有的树莓派主板上都有一个 40 针的 GPIO 头
  • 电压说明

    • 电路板上有两个 5V 引脚和两个 3.3V 引脚,以及多个接地引脚 (0V),这些引脚是不可配置的。其余引脚都是通用 3.3V 引脚,这意味着输出设置为 3.3V,输入允许 3.3V
  • IO 输出

    • 指定为输出引脚的 GPIO 引脚可以设置为高电平 (3.3V) 或低电平 (0V)
  • IO 输入

    • 指定为输入引脚的 GPIO 引脚可以读取为高 (3.3V) 或低 (0V)。这是更容易使用内部上拉或下拉电阻。引脚 GPIO2 和 GPIO3 有固定的上拉电阻,但对于其他引脚,这可以在软件中配置
  • PWM(脉冲宽度调制)

    • 所有引脚均可使用软件控制 PWM
    • GPIO12, GPIO13, GPIO18, GPIO19 可使用硬件控制 PWM
  • SPI

    • SPI 全称为串行外设接口(Serial Peripheral Interface),其是一种高速的,全双工,同步通信总线
    • SPI0: MOSI (GPIO10); MISO (GPIO9); SCLK (GPIO11); CE0 (GPIO8), CE1 (GPIO7)
    • SPI1: MOSI (GPIO20); MISO (GPIO19); SCLK (GPIO21); CE0 (GPIO18); CE1 (GPIO17); CE2 (GPIO16)
  • IIC

    • I2C,中文全称为集成电路总线,是一种串行通信总线,使用多主从架构
    • 数据 (GPI02),时钟 (GPI03)
    • EEPROM 数据:(GPIO0),EEPROM 时钟 (GPI01)
  • Serial

    • 串口是串行接口(serial port)的简称,也称为串行通信接口或 COM 接口
    • TX (GPIO14),RX (GPIO15)
  • python 控制引脚输出


    import RPi.GPIO as GPIO
    import time
    # 初始化
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(20, GPIO.OUT)
    GPIO.setup(21, GPIO.OUT)
    # 高电平
    GPIO.output(20, 0)
    GPIO.output(21, 0)
    # 等待 2 秒
    time.sleep(2)
    # 低电平
    GPIO.output(20, 1)
    GPIO.output(21, 1)

  • GPIO 的各个接口定义如下表所示

标签信号名类型功能备注
5V5VPDC 5V
5V5VPDC 5V
GNDGNDpGND
NCNC--暂不支持
NCNC--暂不支持
18GPIO18I/OGPIO18
GNDGNDpGND
23GPIO23I/OGPIO23
24GPIO24I/OGPIO24
GNDGNDpGND
25GPIO25I/OGPIO25
08GPIO8I/OGPIO8
07GPIO7I/OGPIO7
01GPIO1I/OGPIO1
GNDGNDpGND
12GPIO12I/OGPIO12
GNDGNDpGND
16GPIO16I/OGPIO16
20GPIO20I/OGPIO20
21GPIO21I/OGPIO21
3.33.3VPDC 3.3V
NCNC--暂不支持
03GPIO3I/OGPIO3
04GPIO4I/OGPIO4
GNDGNDpGND
17GPIO17I/OGPIO17
27GPIO27I/OGPIO27
22GPIO22I/OGPIO22
3.33.3VPDC 3.3V
10GPIO10I/OGPIO10
09GPIO9I/OGPIO9
11GPIO11I/OGPIO11
GNDGNDpGND
00GPIO0I/OGPIO0
05GPIO5I/OGPIO5
06GPIO6I/OGPIO6
13GPIO13I/OGPIO13
19GPIO19I/OGPIO19
26GPIO26I/OGPIO26
GNDGNDpGND

注意:

  1. I: 仅作为输入。
  2. I/O: 该功能信号包含输入和输出组合。
  3. 当管角设置为输出端时,它将输出电压 3.3V。
  4. 单个管角的拉电流随管脚数量增加而减小,从约 40mA 减小到 29mA。
  5. 如果某个 GPIO 被设置为输出模式时,输出高电平信号,电路连接如图 2.1.5.2-3 所示,LED 灯将点亮。

  1. 关于功能接口的其他功能表如下图所示,使用其他功能的情况下,IO 功能不可用。

3.2 机械臂底座接口介绍

  • A. 底座的正面如图所示:

底座正面

  1. ① 开关按键
  2. ② 功能接口组一
  3. ③ USB2.0, USB3.0
  4. ④ 电源 DC 接口
  5. ⑤ 网口
  • B. 底座的侧面如图所示:

底座侧面

  1. ① SD 卡卡槽
  2. ② Type C
  3. ③ HDMI
  4. ④ 音频接口

3.3 机械臂底座接口详细说明

  • A. 电源 DC 接口:使用 DC 电源插座,外径 6.5mm,内径 2.0mm;可使用厂家配备的 12V 5A DC 电源适配器给 myCobot280 进行供电。

  • B. 开关按键:红色为开关,I 为开机,O 为关机。

  • C. USB2.0 接口:以串口总线标准 2.0 进行数据连接的接口;用户可以使用 USB 接口拷贝程序文件,也可以使用 USB 接口连接鼠标、键盘等外设。

  • D. USB3.0 接口(蓝色):以串口总线标准 3.0 进行数据连接的接口;用户可以使用 USB 接口拷贝程序文件,也可以使用 USB 接口连接鼠标、键盘等外设。

  • E. 网口:网络数据连接的端口,用户使用 Ethernet 接口可以用于 PC 端与机器人系统的通信交互,也可以用于与其他设备进行以太网通信。

  • F. HDMI 接口:接口为 HDMI D 型接口,连接显示器。HDMI 接口 2 存在优先级,推荐使用 HDMI 接口 1。

  • G. Type C 接口:可用于和 PC 端连接通讯,更新固件使用。

  • H. SD 卡卡槽:SD 卡可以插入和拔出。SD 卡的大小是 32mm×24mmx2.1mm