机械臂运动控制实验
教学目的(教师视角)
学生通过阅读本节材料,从中提炼出有用的信息,完成机械臂运动控制的“示教拖动”的机器人实验。通过本节的学习,学生将能够:
- 通过“研究的方式”自行学习,从而培养独立、自主学习能力。
- 增强面对陌生问题时的解决问题能力,从而提升解决问题的自信心。
- 通过“实践的方式”完成机械臂运动控制的“示教拖动”的机器人实验,从而培养动手能力。
课程目的(学生视角)
本节课的目标只有一个:让机械臂能够动起来。具体来说:
01. 熟悉开发环境
机械臂运动控制实验的准备工作
1.1 机器人系统介绍
-
系统简介
-
Ubuntu 是个人桌面操作系统中使用最广泛的 linux 操作系统。对于初学者来说,熟悉 linux 环境或一些嵌入式硬件操作系统是一个很好的选择。ubuntu 官方网站还发布了树莓派专用操作系统。

-
-
系统功能简介
- 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。连接的方式有两种,
-
第一种方式需要外接显示器对系统进行一些操作,具体步骤如下:
先点击 "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,用户名默认是不填的,成功连接示例如下:
.png)
-
第二种方式不需要连接显示屏,直接用 PC 连接 Ubuntu 系统热点进行远程控制,但是此连接方式并不具备网络冲浪的功能,只能远程操控机械臂系统,具体步骤如下:
PC 选择连接 Ubuntu 系统热点 ElephantRobotics_AP_XXXX ,输入密码 Elephant
.png)
打开 VNC viewer 软件,输入这个 IP 地址 10.42.0.1 ,然后回车,密码是 Elephant,用户名默认是不填的,成功连接示例如下:
.png)
-
-
如何提高流畅性
- 远程连接流畅性取决于所连 WiFi 的流畅性,建议连接稳定的 WiFi 进行远程控制
1.4 SSH
-
SSH 功能介绍
- 简单说,SSH 是一种网络协议,用于计算机之间的加密登录。如果一个用户从本地计算机,使用 SSH 协议登录另一台远程计算机,我们就可以认为,这种登录是安全的,即使被中途截获,密码也不会泄露。
-
SSH 端口说明
- 默认端口 22,无需更改
-
SSH 首次连接
-
按照 2.3 VNC 确认机械臂 IP 地址
-
在个人电脑按下
win + R打开运行界面,在输入框输入cmd

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

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

- 输入密码
Elephant

- 如上图所示,已成功远程 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" ,断开默认热点连接
.png)
将网线连接到机械臂的网络端口

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

-
如何设定默认 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 依赖:
| Package | Version |
|---|---|
| pymycobot | 3.0.9 |
| pyserial | 3.5 |
| numpy | 1.23.5 |
| opencv-contrib-python | 4.7.0.72 |
| rospkg | 1.4.0 |
| rospkg-modules | 1.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。
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 的各个接口定义如下表所示:
| 标签 | 信号名 | 类型 | 功能 | 备注 |
|---|---|---|---|---|
| 5V | 5V | P | DC 5V | |
| 5V | 5V | P | DC 5V | |
| GND | GND | p | GND | |
| NC | NC | - | - | 暂不支持 |
| NC | NC | - | - | 暂不支持 |
| 18 | GPIO18 | I/O | GPIO18 | |
| GND | GND | p | GND | |
| 23 | GPIO23 | I/O | GPIO23 | |
| 24 | GPIO24 | I/O | GPIO24 | |
| GND | GND | p | GND | |
| 25 | GPIO25 | I/O | GPIO25 | |
| 08 | GPIO8 | I/O | GPIO8 | |
| 07 | GPIO7 | I/O | GPIO7 | |
| 01 | GPIO1 | I/O | GPIO1 | |
| GND | GND | p | GND | |
| 12 | GPIO12 | I/O | GPIO12 | |
| GND | GND | p | GND | |
| 16 | GPIO16 | I/O | GPIO16 | |
| 20 | GPIO20 | I/O | GPIO20 | |
| 21 | GPIO21 | I/O | GPIO21 | |
| 3.3 | 3.3V | P | DC 3.3V | |
| NC | NC | - | - | 暂不支持 |
| 03 | GPIO3 | I/O | GPIO3 | |
| 04 | GPIO4 | I/O | GPIO4 | |
| GND | GND | p | GND | |
| 17 | GPIO17 | I/O | GPIO17 | |
| 27 | GPIO27 | I/O | GPIO27 | |
| 22 | GPIO22 | I/O | GPIO22 | |
| 3.3 | 3.3V | P | DC 3.3V | |
| 10 | GPIO10 | I/O | GPIO10 | |
| 09 | GPIO9 | I/O | GPIO9 | |
| 11 | GPIO11 | I/O | GPIO11 | |
| GND | GND | p | GND | |
| 00 | GPIO0 | I/O | GPIO0 | |
| 05 | GPIO5 | I/O | GPIO5 | |
| 06 | GPIO6 | I/O | GPIO6 | |
| 13 | GPIO13 | I/O | GPIO13 | |
| 19 | GPIO19 | I/O | GPIO19 | |
| 26 | GPIO26 | I/O | GPIO26 | |
| GND | GND | p | GND |
注意:
- I: 仅作为输入。
- I/O: 该功能信号包含输入和输出组合。
- 当管角设置为输出端时,它将输出电压 3.3V。
- 单个管角的拉电流随管脚数量增加而减小,从约 40mA 减小到 29mA。
- 如果某个 GPIO 被设置为输出模式时,输出高电平信号,电路连接如图 2.1.5.2-3 所示,LED 灯将点亮。
- 关于功能接口的其他功能表如下图所示,使用其他功能的情况下,IO 功能不可用。
3.2 机械臂底座接口介绍
- A. 底座的正面如图所示:

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

底座侧面
- ① SD 卡卡槽
- ② Type C
- ③ HDMI
- ④ 音频接口
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

