├── 彩纸 ├── 彩纸图.png ├── 彩纸打印-V4.pdf ├── 彩纸打印-V4.docx ├── ~$纸打印-V4.docx └── ~$打印1-亚马逊.docx ├── agent_demo_20240523 ├── temp │ ├── tts.wav │ ├── vl_now.jpg │ ├── vl_now_viz.jpg │ ├── speech_record.wav │ └── record.txt ├── asset │ ├── SimHei.ttf │ └── welcome.wav ├── API_KEY.py ├── utils_camera.py ├── utils_pump.py ├── utils_led.py ├── utils_llm.py ├── utils_tts.py ├── agent_go.py ├── utils_vlm_move.py ├── utils_agent.py ├── README.ipynb ├── utils_asr.py ├── utils_vlm.py ├── drag_trial_teaching.py ├── utils_drag_teaching.py └── utils_robot.py └── README.md /彩纸/彩纸图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TommyZihao/Mycobot_Tutorials/HEAD/彩纸/彩纸图.png -------------------------------------------------------------------------------- /彩纸/彩纸打印-V4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TommyZihao/Mycobot_Tutorials/HEAD/彩纸/彩纸打印-V4.pdf -------------------------------------------------------------------------------- /彩纸/彩纸打印-V4.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TommyZihao/Mycobot_Tutorials/HEAD/彩纸/彩纸打印-V4.docx -------------------------------------------------------------------------------- /agent_demo_20240523/temp/tts.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TommyZihao/Mycobot_Tutorials/HEAD/agent_demo_20240523/temp/tts.wav -------------------------------------------------------------------------------- /agent_demo_20240523/asset/SimHei.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TommyZihao/Mycobot_Tutorials/HEAD/agent_demo_20240523/asset/SimHei.ttf -------------------------------------------------------------------------------- /agent_demo_20240523/asset/welcome.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TommyZihao/Mycobot_Tutorials/HEAD/agent_demo_20240523/asset/welcome.wav -------------------------------------------------------------------------------- /agent_demo_20240523/temp/vl_now.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TommyZihao/Mycobot_Tutorials/HEAD/agent_demo_20240523/temp/vl_now.jpg -------------------------------------------------------------------------------- /agent_demo_20240523/temp/vl_now_viz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TommyZihao/Mycobot_Tutorials/HEAD/agent_demo_20240523/temp/vl_now_viz.jpg -------------------------------------------------------------------------------- /agent_demo_20240523/temp/speech_record.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TommyZihao/Mycobot_Tutorials/HEAD/agent_demo_20240523/temp/speech_record.wav -------------------------------------------------------------------------------- /彩纸/~$纸打印-V4.docx: -------------------------------------------------------------------------------- 1 | Microsoft Office UserMicrosoft Office User -------------------------------------------------------------------------------- /彩纸/~$打印1-亚马逊.docx: -------------------------------------------------------------------------------- 1 | Microsoft Office UserMicrosoft Office User -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 同济子豪兄大象机械臂Mycobot 280 Pi教程。机器人运动学、逆运动学、Python控制、ROS、具身智能 2 | 3 | 作者:同济子豪兄 4 | 5 | # 机械臂+大模型+多模态=人机协作具身智能体 6 | 7 | ![架构图](https://github.com/TommyZihao/Mycobot_Tutorials/assets/36354458/d1e52601-1ebd-4f44-a84c-ab1c8a65ac2b) 8 | 9 | 目标:听得懂人话、看得懂图像、拎得清动作 10 | 11 | 智能体Agent大语言模型:Yi-Large、Claude 3 Opus 12 | 13 | 多模态视觉理解大模型:GPT4v、GPT4o、Yi-Vision、Claude 3 Opus、通义千问Qwen-VL-Max 14 | 15 | 机械臂:大象机器人 Mycobot 280 Pi 16 | 17 | 开发板:树莓派4B Ubuntu 20.04 18 | -------------------------------------------------------------------------------- /agent_demo_20240523/API_KEY.py: -------------------------------------------------------------------------------- 1 | # API_KEY.py 2 | # 同济子豪兄 2024-5-22 3 | # 各种开放平台的KEY,不要外传 4 | 5 | # 零一万物大模型开放平台 6 | # https://platform.lingyiwanwu.com 7 | YI_KEY = "f8144ffaff7c4597910bb5d3aXXX" 8 | 9 | # 百度智能云千帆ModelBuilder 10 | # https://qianfan.cloud.baidu.com 11 | QIANFAN_ACCESS_KEY = "ALTAKRELRxSapDDBCwXXX" 12 | QIANFAN_SECRET_KEY = "3737d9da82de4f259db7aXXX" 13 | 14 | # 百度智能云千帆AppBuilder-SDK 15 | APPBUILDER_TOKEN = "bce-v3/ALTAK-7jr20xkZl4cDmhbQKA4ml/f560e5dc3XXXXXXX059a9f681743d1df4" 16 | -------------------------------------------------------------------------------- /agent_demo_20240523/utils_camera.py: -------------------------------------------------------------------------------- 1 | # utils_camera.py 2 | # 同济子豪兄 2024-5-22 3 | # 开启摄像头,调用摄像头实时画面,按q键退出 4 | 5 | import cv2 6 | import numpy as np 7 | 8 | def check_camera(): 9 | ''' 10 | 开启摄像头,调用摄像头实时画面,按q键退出 11 | ''' 12 | print('开启摄像头') 13 | cap = cv2.VideoCapture(0) 14 | 15 | while(True): 16 | ret, frame = cap.read() 17 | 18 | # gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 19 | 20 | cv2.imshow('frame', frame) 21 | if cv2.waitKey(1) & 0xFF == ord('q'): 22 | break 23 | 24 | cap.release() 25 | cv2.destroyAllWindows() -------------------------------------------------------------------------------- /agent_demo_20240523/utils_pump.py: -------------------------------------------------------------------------------- 1 | # utils_pump.py 2 | # 同济子豪兄 2024-5-22 3 | # GPIO引脚、吸泵相关函数 4 | 5 | print('导入吸泵控制模块') 6 | import RPi.GPIO as GPIO 7 | import time 8 | 9 | # 初始化GPIO 10 | GPIO.setwarnings(False) # 不打印 warning 信息 11 | GPIO.setmode(GPIO.BCM) 12 | GPIO.setup(20, GPIO.OUT) 13 | GPIO.setup(21, GPIO.OUT) 14 | GPIO.output(20, 1) # 关闭吸泵电磁阀 15 | 16 | def pump_on(): 17 | ''' 18 | 开启吸泵 19 | ''' 20 | print(' 开启吸泵') 21 | GPIO.output(20, 0) 22 | 23 | def pump_off(): 24 | ''' 25 | 关闭吸泵,吸泵放气,释放物体 26 | ''' 27 | print(' 关闭吸泵') 28 | GPIO.output(20, 1) # 关闭吸泵电磁阀 29 | time.sleep(0.05) 30 | GPIO.output(21, 0) # 打开泄气阀门 31 | time.sleep(0.2) 32 | GPIO.output(21, 1) 33 | time.sleep(0.05) 34 | GPIO.output(21, 0) # 再一次泄气,确保物体释放 35 | time.sleep(0.2) 36 | GPIO.output(21, 1) 37 | time.sleep(0.05) -------------------------------------------------------------------------------- /agent_demo_20240523/utils_led.py: -------------------------------------------------------------------------------- 1 | # utils_led.py 2 | # 同济子豪兄 2024-5-22 3 | # 大模型控制LED灯颜色 4 | 5 | from utils_llm import llm_qianfan, llm_yi 6 | from utils_robot import mc 7 | 8 | print('导入LED灯控制模块') 9 | 10 | # 备选颜色 11 | # 贝加尔湖、中国红、大海、绿叶、金子、蓝宝石、小猪佩奇、墨绿色、黑色 12 | 13 | # 系统提示词 14 | SYS_PROMPT = '我即将说的这句话中包含一个目标物体,帮我把这个物体的一种可能的颜色,以0-255的RGB像素值形式返回给我,整理成元组格式,例如(255, 30, 60),直接回复元组本身,以括号开头,不要回复任何中文内容,下面是这句话:' 15 | 16 | def llm_led(PROMPT_LED='帮我把LED灯的颜色改为贝加尔湖的颜色'): 17 | ''' 18 | 大模型控制LED灯颜色 19 | ''' 20 | 21 | PROMPT = SYS_PROMPT + PROMPT_LED 22 | 23 | n = 1 24 | while n < 5: 25 | try: 26 | # 调用大模型API 27 | # response = llm_qianfan(PROMPT) 28 | response = llm_yi(PROMPT) 29 | 30 | # 提取颜色 31 | rgb_tuple = eval(response) 32 | 33 | # 设置LED灯的RGB颜色 34 | mc.set_color(rgb_tuple[0], rgb_tuple[1], rgb_tuple[2]) 35 | print('LED灯颜色修改成功', rgb_tuple) 36 | 37 | break 38 | 39 | except Exception as e: 40 | print('大模型返回json结构错误,再尝试一次', e) 41 | n += 1 -------------------------------------------------------------------------------- /agent_demo_20240523/utils_llm.py: -------------------------------------------------------------------------------- 1 | # utils_llm.py 2 | # 同济子豪兄 2024-5-22 3 | # 调用大语言模型API 4 | 5 | print('导入大模型API模块') 6 | 7 | 8 | import os 9 | 10 | import qianfan 11 | def llm_qianfan(PROMPT='你好,你是谁?'): 12 | ''' 13 | 百度智能云千帆大模型平台API 14 | ''' 15 | 16 | # 传入 ACCESS_KEY 和 SECRET_KEY 17 | os.environ["QIANFAN_ACCESS_KEY"] = QIANFAN_ACCESS_KEY 18 | os.environ["QIANFAN_SECRET_KEY"] = QIANFAN_SECRET_KEY 19 | 20 | # 选择大语言模型 21 | MODEL = "ERNIE-Bot-4" 22 | # MODEL = "ERNIE Speed" 23 | # MODEL = "ERNIE-Lite-8K" 24 | # MODEL = 'ERNIE-Tiny-8K' 25 | 26 | chat_comp = qianfan.ChatCompletion(model=MODEL) 27 | 28 | # 输入给大模型 29 | resp = chat_comp.do( 30 | messages=[{"role": "user", "content": PROMPT}], 31 | top_p=0.8, 32 | temperature=0.3, 33 | penalty_score=1.0 34 | ) 35 | 36 | response = resp["result"] 37 | return response 38 | 39 | import openai 40 | from openai import OpenAI 41 | from API_KEY import * 42 | def llm_yi(PROMPT='你好,你是谁?'): 43 | ''' 44 | 零一万物大模型API 45 | ''' 46 | 47 | API_BASE = "https://api.lingyiwanwu.com/v1" 48 | API_KEY = YI_KEY 49 | 50 | MODEL = 'yi-large' 51 | # MODEL = 'yi-medium' 52 | # MODEL = 'yi-spark' 53 | 54 | # 访问大模型API 55 | client = OpenAI(api_key=API_KEY, base_url=API_BASE) 56 | completion = client.chat.completions.create(model=MODEL, messages=[{"role": "user", "content": PROMPT}]) 57 | result = completion.choices[0].message.content.strip() 58 | return result 59 | 60 | -------------------------------------------------------------------------------- /agent_demo_20240523/utils_tts.py: -------------------------------------------------------------------------------- 1 | # utils_tts.py 2 | # 同济子豪兄 2024-5-23 3 | # 语音合成 4 | 5 | print('导入语音合成模块') 6 | 7 | import os 8 | import appbuilder 9 | from API_KEY import * 10 | import pyaudio 11 | import wave 12 | 13 | tts_ab = appbuilder.TTS() 14 | 15 | def tts(TEXT='我是子豪兄的麒麟臂', tts_wav_path = 'temp/tts.wav'): 16 | ''' 17 | 语音合成TTS,生成wav音频文件 18 | ''' 19 | inp = appbuilder.Message(content={"text": TEXT}) 20 | out = tts_ab.run(inp, model="paddlespeech-tts", audio_type="wav") 21 | with open(tts_wav_path, "wb") as f: 22 | f.write(out.content["audio_binary"]) 23 | # print("TTS语音合成,导出wav音频文件至:{}".format(tts_wav_path)) 24 | 25 | def play_wav(wav_file='asset/tts.wav'): 26 | ''' 27 | 播放wav音频文件 28 | ''' 29 | prompt = 'aplay -t wav {} -q'.format(wav_file) 30 | os.system(prompt) 31 | 32 | # def play_wav(wav_file='temp/tts.wav'): 33 | # ''' 34 | # 播放wav文件 35 | # ''' 36 | # wf = wave.open(wav_file, 'rb') 37 | 38 | # # 实例化PyAudio 39 | # p = pyaudio.PyAudio() 40 | 41 | # # 打开流 42 | # stream = p.open(format=p.get_format_from_width(wf.getsampwidth()), 43 | # channels=wf.getnchannels(), 44 | # rate=wf.getframerate(), 45 | # output=True) 46 | 47 | # chunk_size = 1024 48 | # # 读取数据 49 | # data = wf.readframes(chunk_size) 50 | 51 | # # 播放音频 52 | # while data != b'': 53 | # stream.write(data) 54 | # data = wf.readframes(chunk_size) 55 | 56 | # # 停止流,关闭流和PyAudio 57 | # stream.stop_stream() 58 | # stream.close() 59 | # p.terminate() -------------------------------------------------------------------------------- /agent_demo_20240523/agent_go.py: -------------------------------------------------------------------------------- 1 | # agent_go.py 2 | # 同济子豪兄 2024-5-22 3 | # 看懂“图像”、听懂“人话”、指哪打哪的机械臂 4 | # 机械臂+大模型+多模态+语音识别=具身智能体Agent 5 | 6 | print('\n听得懂人话、看得懂图像、拎得清动作的具身智能机械臂!') 7 | print('同济子豪兄 2024-5-22 \n') 8 | 9 | # 导入常用函数 10 | from utils_asr import * # 录音+语音识别 11 | from utils_robot import * # 连接机械臂 12 | from utils_llm import * # 大语言模型API 13 | from utils_led import * # 控制LED灯颜色 14 | from utils_camera import * # 摄像头 15 | from utils_robot import * # 机械臂运动 16 | from utils_pump import * # GPIO、吸泵 17 | from utils_vlm_move import * # 多模态大模型识别图像,吸泵吸取并移动物体 18 | from utils_drag_teaching import * # 拖动示教 19 | from utils_agent import * # 智能体Agent编排 20 | from utils_tts import * # 语音合成模块 21 | 22 | # print('播放欢迎词') 23 | play_wav('asset/welcome.wav') 24 | 25 | def agent_play(): 26 | ''' 27 | 主函数,语音控制机械臂智能体编排动作 28 | ''' 29 | # 归零 30 | back_zero() 31 | 32 | # print('测试摄像头') 33 | # check_camera() 34 | 35 | # 输入指令 36 | # 先回到原点,再把LED灯改为墨绿色,然后把绿色方块放在篮球上 37 | start_record_ok = input('是否开启录音,按r开始录制,按k打字输入,按c输入默认指令') 38 | if start_record_ok == 'r': 39 | record() # 录音 40 | order = speech_recognition() # 语音识别 41 | elif start_record_ok == 'k': 42 | order = input('请输入指令') 43 | elif start_record_ok == 'c': 44 | order = '先归零,再摇头,然后把绿色方块放在篮球上' 45 | 46 | # 智能体Agent编排动作 47 | agent_plan_output = eval(agent_plan(order)) 48 | 49 | print('智能体编排动作如下\n', agent_plan_output) 50 | # plan_ok = input('是否继续?按c继续,按q退出') 51 | plan_ok = 'c' 52 | if plan_ok == 'c': 53 | response = agent_plan_output['response'] # 获取机器人想对我说的话 54 | tts(response) # 语音合成,导出wav音频文件 55 | play_wav('temp/tts.wav') # 播放语音合成音频文件 56 | for each in agent_plan_output['function']: # 运行智能体规划编排的每个函数 57 | print('开始执行动作', each) 58 | eval(each) 59 | elif plan_ok =='q': 60 | exit() 61 | 62 | if __name__ == '__main__': 63 | agent_play() 64 | 65 | -------------------------------------------------------------------------------- /agent_demo_20240523/utils_vlm_move.py: -------------------------------------------------------------------------------- 1 | # utils_vlm_move.py 2 | # 同济子豪兄 2024-5-22 3 | # 输入指令,多模态大模型识别图像,吸泵吸取并移动物体 4 | 5 | # print('神行太保:能看懂“图像”、听懂“人话”的机械臂') 6 | 7 | from utils_robot import * 8 | from utils_asr import * 9 | from utils_vlm import * 10 | 11 | import time 12 | 13 | def vlm_move(PROMPT='帮我把绿色方块放在小猪佩奇上', input_way='keyboard'): 14 | ''' 15 | 多模态大模型识别图像,吸泵吸取并移动物体 16 | input_way:speech语音输入,keyboard键盘输入 17 | ''' 18 | 19 | print('多模态大模型识别图像,吸泵吸取并移动物体') 20 | 21 | # 机械臂归零 22 | print('机械臂归零') 23 | mc.send_angles([0, 0, 0, 0, 0, 0], 50) 24 | time.sleep(3) 25 | 26 | ## 第一步:完成手眼标定 27 | print('第一步:完成手眼标定') 28 | 29 | ## 第二步:发出指令 30 | # PROMPT_BACKUP = '帮我把绿色方块放在小猪佩奇上' # 默认指令 31 | 32 | # if input_way == 'keyboard': 33 | # PROMPT = input('第二步:输入指令') 34 | # if PROMPT == '': 35 | # PROMPT = PROMPT_BACKUP 36 | # elif input_way == 'speech': 37 | # record() # 录音 38 | # PROMPT = speech_recognition() # 语音识别 39 | print('第二步,给出的指令是:', PROMPT) 40 | 41 | ## 第三步:拍摄俯视图 42 | print('第三步:拍摄俯视图') 43 | top_view_shot(check=False) 44 | 45 | ## 第四步:将图片输入给多模态视觉大模型 46 | print('第四步:将图片输入给多模态视觉大模型') 47 | img_path = 'temp/vl_now.jpg' 48 | 49 | n = 1 50 | while n < 5: 51 | try: 52 | print(' 尝试第 {} 次访问多模态大模型'.format(n)) 53 | result = yi_vision_api(PROMPT, img_path='temp/vl_now.jpg') 54 | print(' 多模态大模型调用成功!') 55 | print(result) 56 | break 57 | except Exception as e: 58 | print(' 多模态大模型返回数据结构错误,再尝试一次', e) 59 | n += 1 60 | 61 | ## 第五步:视觉大模型输出结果后处理和可视化 62 | print('第五步:视觉大模型输出结果后处理和可视化') 63 | START_X_CENTER, START_Y_CENTER, END_X_CENTER, END_Y_CENTER = post_processing_viz(result, img_path, check=True) 64 | 65 | ## 第六步:手眼标定转换为机械臂坐标 66 | print('第六步:手眼标定,将像素坐标转换为机械臂坐标') 67 | # 起点,机械臂坐标 68 | START_X_MC, START_Y_MC = eye2hand(START_X_CENTER, START_Y_CENTER) 69 | # 终点,机械臂坐标 70 | END_X_MC, END_Y_MC = eye2hand(END_X_CENTER, END_Y_CENTER) 71 | 72 | ## 第七步:吸泵吸取移动物体 73 | print('第七步:吸泵吸取移动物体') 74 | pump_move(mc=mc, XY_START=[START_X_MC, START_Y_MC], XY_END=[END_X_MC, END_Y_MC]) 75 | 76 | ## 第八步:收尾 77 | print('第八步:任务完成') 78 | GPIO.cleanup() # 释放GPIO pin channel 79 | cv2.destroyAllWindows() # 关闭所有opencv窗口 80 | # exit() 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /agent_demo_20240523/utils_agent.py: -------------------------------------------------------------------------------- 1 | # utils_agent.py 2 | # 同济子豪兄 2024-5-23 3 | # Agent智能体相关函数 4 | 5 | from utils_llm import * 6 | 7 | AGENT_SYS_PROMPT = ''' 8 | 你是我的机械臂助手,机械臂内置了一些函数,请你根据我的指令,以json形式输出要运行的对应函数和你给我的回复 9 | 10 | 【以下是所有内置函数介绍】 11 | 机械臂位置归零,所有关节回到原点:back_zero() 12 | 放松机械臂,所有关节都可以自由手动拖拽活动:back_zero() 13 | 做出摇头动作:head_shake() 14 | 做出点头动作:head_nod() 15 | 做出跳舞动作:head_dance() 16 | 打开吸泵:pump_on() 17 | 关闭吸泵:pump_off() 18 | 移动到指定XY坐标,比如移动到X坐标150,Y坐标-120:move_to_coords(X=150, Y=-120) 19 | 指定关节旋转,比如关节1旋转到60度,总共有6个关节:single_joint_move(1, 60) 20 | 移动至俯视姿态:move_to_top_view() 21 | 拍一张俯视图:top_view_shot() 22 | 开启摄像头,在屏幕上实时显示摄像头拍摄的画面:check_camera() 23 | LED灯改变颜色,比如:llm_led('帮我把LED灯的颜色改为贝加尔湖的颜色') 24 | 将一个物体移动到另一个物体的位置上,比如:vlm_move('帮我把红色方块放在小猪佩奇上') 25 | 拖动示教,我可以拽着机械臂运动,然后机械臂模仿复现出一样的动作:drag_teach() 26 | 27 | 【输出json格式】 28 | 你直接输出json即可,从{开始,不要输出包含```json的开头或结尾 29 | 在'function'键中,输出函数名列表,列表中每个元素都是字符串,代表要运行的函数名称和参数。每个函数既可以单独运行,也可以和其他函数先后运行。列表元素的先后顺序,表示执行函数的先后顺序 30 | 在'response'键中,根据我的指令和你编排的动作,以第一人称,输出你回复我的话,要简短一些,可以幽默和发散,用上歌词、台词、互联网热梗、名场面。比如李云龙的台词、甄嬛传的台词、练习时长两年半。 31 | 32 | 【以下是一些具体的例子】 33 | 我的指令:回到原点。你输出:{'function':['back_zero()'], 'response':'回家吧,回到最初的美好'} 34 | 我的指令:先回到原点,然后跳舞。你输出:{'function':['back_zero()', 'head_dance()'], 'response':'我的舞姿,练习时长两年半'} 35 | 我的指令:先回到原点,然后移动到180, -90坐标。你输出:{'function':['back_zero()', 'move_to_coords(X=180, Y=-90)'], 'response':'精准不,老子打的就是精锐'} 36 | 我的指令:先打开吸泵,再把关节2旋转到30度。你输出:{'function':['pump_on()', single_joint_move(2, 30)], 'response':'你之前做的指星笔,就是通过关节2调俯仰角'} 37 | 我的指令:移动到X为160,Y为-30的地方。你输出:{'function':['move_to_coords(X=160, Y=-30)'], 'response':'坐标移动已完成'} 38 | 我的指令:拍一张俯视图,然后把LED灯的颜色改为黄金的颜色。你输出:{'function':['top_view_shot()', llm_led('把LED灯的颜色改为黄金的颜色')], 'response':'人工智能未来比黄金值钱,你信不信'} 39 | 我的指令:帮我把绿色方块放在小猪佩奇上面。你输出:{'function':[vlm_move('帮我把绿色方块放在小猪佩奇上面')], 'response':'它的弟弟乔治呢?'} 40 | 我的指令:帮我把红色方块放在李云龙的脸上。你输出:{'function':[vlm_move('帮我把红色方块放在李云龙的脸上')], 'response':'你他娘的真是个天才'} 41 | 我的指令:关闭吸泵,打开摄像头。你输出:{'function':[pump_off(), check_camera()], 'response':'你是我的眼,带我阅读浩瀚的书海'} 42 | 我的指令:先归零,再把LED灯的颜色改为墨绿色。你输出:{'function':[back_zero(), llm_led('把LED灯的颜色改为墨绿色')], 'response':'这种墨绿色,很像蜀南竹海的竹子'} 43 | 我的指令:我拽着你运动,然后你模仿复现出这个运动。你输出:{'function':['drag_teach()'], 'response':'你有本事拽一个鸡你太美'} 44 | 我的指令:开启拖动示教。你输出:{'function':['drag_teach()'], 'response':'你要我模仿我自己?'} 45 | 我的指令:先回到原点,再打开吸泵,把LED灯的颜色改成中国红,最后把绿色方块移动到摩托车上。你输出:{'function':['back_zero()', 'pump_on()', llm_led('把LED灯的颜色改为中国红色', vlm_move('把绿色方块移动到摩托车上'))], 'response':'如果奇迹有颜色,那一定是中国红'} 46 | 47 | 【一些李云龙相关的台词,如果和李云龙相关,可以在response中提及对应的台词】 48 | 学习?学个屁 49 | 给你半斤地瓜烧 50 | 老子打的就是精锐 51 | 二营长,你的意大利炮呢 52 | 你他娘的真是个天才 53 | 咱老李也是十里八乡的俊后生 54 | 不报此仇,我李云龙誓不为人 55 | 你猜旅长怎么说 56 | 逢敌必亮剑,绝不含糊! 57 | 老子当初怎么教他打枪,现在就教他怎么打仗! 58 | 你咋就不敢跟旅长干一架呢? 59 | 你猪八戒戴眼镜充什么大学生啊? 60 | 我李云龙八岁习武,南拳北腿略知一二。 61 | 死,也要死在冲锋的路上! 62 | 63 | 64 | 【一些小猪佩奇相关的台词】 65 | 这是我的弟弟乔治 66 | 67 | 【我现在的指令是】 68 | ''' 69 | 70 | def agent_plan(AGENT_PROMPT='先回到原点,再把LED灯改为墨绿色,然后把绿色方块放在篮球上'): 71 | print('Agent智能体编排动作') 72 | PROMPT = AGENT_SYS_PROMPT + AGENT_PROMPT 73 | agent_plan = llm_yi(PROMPT) 74 | return agent_plan 75 | -------------------------------------------------------------------------------- /agent_demo_20240523/README.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "c8a0febf-1001-4a87-b873-06bc1471187c", 6 | "metadata": {}, 7 | "source": [ 8 | "# 语音控制智能体\n", 9 | "\n", 10 | "同济子豪兄 2024-5-23" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "id": "bb2091f1-1d00-40bc-9432-9d7cd3d9157e", 16 | "metadata": {}, 17 | "source": [ 18 | "## 首先要做\n", 19 | "\n", 20 | "- 音频输出选择HDMI显示屏\n", 21 | "\n", 22 | "- 手眼标定" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": null, 28 | "id": "e2e53b1d-d9f6-482c-95e7-38f08955f5bd", 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "id": "e2c144d1-059c-40d1-b69b-8485cb6686c5", 36 | "metadata": {}, 37 | "source": [ 38 | "# 智能体Agent能够调用的函数" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 2, 44 | "id": "1b933878-c06f-426d-8ca3-d4b5ddade0ac", 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "# 函数一:归零\n", 49 | "# back_zero()\n", 50 | "\n", 51 | "# 函数二:放松机械臂\n", 52 | "# relax_arms()\n", 53 | "\n", 54 | "# 函数三:摇头\n", 55 | "# head_shake()\n", 56 | "\n", 57 | "# 函数四:点头\n", 58 | "# head_nod()\n", 59 | "\n", 60 | "# 函数五:跳舞\n", 61 | "# head_dance()\n", 62 | "\n", 63 | "# 函数六:开启吸泵\n", 64 | "# pump_on()\n", 65 | "\n", 66 | "# 函数七:关闭吸泵\n", 67 | "# pump_off()\n", 68 | "\n", 69 | "# 函数八:移动到指定坐标\n", 70 | "# move_to_coords(X=150, Y=-120)\n", 71 | "\n", 72 | "# 函数九:指定关节旋转\n", 73 | "# single_joint_move(1, 60)\n", 74 | "\n", 75 | "# 函数十:移动至俯视姿态\n", 76 | "# move_to_top_view()\n", 77 | "\n", 78 | "# 函数十一:拍一张俯视图\n", 79 | "# top_view_shot()\n", 80 | "\n", 81 | "# 函数十二:开启摄像头\n", 82 | "# check_camera()\n", 83 | "\n", 84 | "# 函数十三:LED灯变颜色\n", 85 | "# llm_led('帮我把LED灯的颜色改为贝加尔湖的颜色')\n", 86 | "\n", 87 | "# 函数十四:移动物体\n", 88 | "# vlm_move(PROMPT='帮我把红色方块放在小猪佩奇上')\n", 89 | "\n", 90 | "# 函数十五:拖动示教\n", 91 | "# drag_teach()" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "id": "d1d0c3be-3080-4543-a943-adb10e19e79b", 98 | "metadata": {}, 99 | "outputs": [], 100 | "source": [] 101 | } 102 | ], 103 | "metadata": { 104 | "kernelspec": { 105 | "display_name": "Python 3 (ipykernel)", 106 | "language": "python", 107 | "name": "python3" 108 | }, 109 | "language_info": { 110 | "codemirror_mode": { 111 | "name": "ipython", 112 | "version": 3 113 | }, 114 | "file_extension": ".py", 115 | "mimetype": "text/x-python", 116 | "name": "python", 117 | "nbconvert_exporter": "python", 118 | "pygments_lexer": "ipython3", 119 | "version": "3.10.14" 120 | } 121 | }, 122 | "nbformat": 4, 123 | "nbformat_minor": 5 124 | } 125 | -------------------------------------------------------------------------------- /agent_demo_20240523/utils_asr.py: -------------------------------------------------------------------------------- 1 | # utils_asr.py 2 | # 同济子豪兄 2024-5-22 3 | # 录音+语音识别 4 | 5 | print('导入录音+语音识别模块') 6 | 7 | import pyaudio 8 | import wave 9 | import numpy as np 10 | import os 11 | import sys 12 | from API_KEY import * 13 | 14 | # 确定麦克风索引号 15 | # import sounddevice as sd 16 | # print(sd.query_devices()) 17 | 18 | def record(): 19 | print('开始五秒录音') 20 | os.system('arecord -D "plughw:1,0" -f dat -c 1 -r 16000 -d 5 temp/speech_record.wav') 21 | 22 | def record_auto(MIC_INDEX=1): 23 | ''' 24 | 开启麦克风录音,保存至'temp/speech_record.wav'音频文件 25 | 音量超过阈值自动开始录音,低于阈值一段时间后自动停止录音 26 | MIC_INDEX:麦克风设备索引号 27 | ''' 28 | 29 | CHUNK = 1024 # 采样宽度 30 | RATE = 16000 # 采样率 31 | 32 | QUIET_DB = 2000 # 分贝阈值,大于则开始录音,否则结束 33 | delay_time = 1 # 声音降至分贝阈值后,经过多长时间,自动终止录音 34 | 35 | FORMAT = pyaudio.paInt16 36 | CHANNELS = 1 if sys.platform == 'darwin' else 2 # 采样通道数 37 | 38 | # 初始化录音 39 | p = pyaudio.PyAudio() 40 | stream = p.open(format=FORMAT, 41 | channels=CHANNELS, 42 | rate=RATE, 43 | input=True, 44 | frames_per_buffer=CHUNK, 45 | input_device_index=MIC_INDEX 46 | ) 47 | 48 | frames = [] # 所有音频帧 49 | 50 | flag = False # 是否已经开始录音 51 | quiet_flag = False # 当前音量小于阈值 52 | 53 | temp_time = 0 # 当前时间是第几帧 54 | last_ok_time = 0 # 最后正常是第几帧 55 | START_TIME = 0 # 开始录音是第几帧 56 | END_TIME = 0 # 结束录音是第几帧 57 | 58 | print('可以说话啦!') 59 | 60 | while True: 61 | 62 | # 获取当前chunk的声音 63 | data = stream.read(CHUNK, exception_on_overflow=False) 64 | frames.append(data) 65 | # 获取当前chunk的音量分贝值 66 | temp_volume = np.max(np.frombuffer(data, dtype=np.short)) 67 | 68 | if temp_volume > QUIET_DB and flag==False: 69 | print("音量高于阈值,开始录音") 70 | flag =True 71 | START_TIME = temp_time 72 | last_ok_time = temp_time 73 | 74 | if flag: # 录音中的各种情况 75 | 76 | if(temp_volume < QUIET_DB and quiet_flag==False): 77 | print("录音中,当前音量低于阈值") 78 | quiet_flag = True 79 | last_ok_time = temp_time 80 | 81 | if(temp_volume > QUIET_DB): 82 | # print('录音中,当前音量高于阈值,正常录音') 83 | quiet_flag = False 84 | last_ok_time = temp_time 85 | 86 | if(temp_time > last_ok_time + delay_time*15 and quiet_flag==True): 87 | print("音量低于阈值{:.2f}秒后,检测当前音量".format(delay_time)) 88 | if(quiet_flag and temp_volume < QUIET_DB): 89 | print("当前音量仍然小于阈值,录音结束") 90 | END_TIME = temp_time 91 | break 92 | else: 93 | print("当前音量重新高于阈值,继续录音中") 94 | quiet_flag = False 95 | last_ok_time = temp_time 96 | 97 | # print('当前帧 {} 音量 {}'.format(temp_time+1, temp_volume)) 98 | temp_time += 1 99 | if temp_time > 150: # 超时直接退出 100 | END_TIME = temp_time 101 | print('超时,录音结束') 102 | break 103 | 104 | # 停止录音 105 | stream.stop_stream() 106 | stream.close() 107 | p.terminate() 108 | 109 | # 导出wav音频文件 110 | output_path = 'temp/speech_record.wav' 111 | wf = wave.open(output_path, 'wb') 112 | wf.setnchannels(CHANNELS) 113 | wf.setsampwidth(p.get_sample_size(FORMAT)) 114 | wf.setframerate(RATE) 115 | wf.writeframes(b''.join(frames[START_TIME-2:END_TIME])) 116 | wf.close() 117 | print('保存录音文件', output_path) 118 | 119 | import appbuilder 120 | # 配置密钥 121 | os.environ["APPBUILDER_TOKEN"] = APPBUILDER_TOKEN 122 | asr = appbuilder.ASR() # 语音识别组件 123 | def speech_recognition(audio_path='temp/speech_record.wav'): 124 | ''' 125 | AppBuilder-SDK语音识别组件 126 | ''' 127 | 128 | # 载入wav音频文件 129 | with wave.open(audio_path, 'rb') as wav_file: 130 | 131 | # 获取音频文件的基本信息 132 | num_channels = wav_file.getnchannels() 133 | sample_width = wav_file.getsampwidth() 134 | framerate = wav_file.getframerate() 135 | num_frames = wav_file.getnframes() 136 | 137 | # 获取音频数据 138 | frames = wav_file.readframes(num_frames) 139 | 140 | # 向API发起请求 141 | content_data = {"audio_format": "wav", "raw_audio": frames, "rate": 16000} 142 | message = appbuilder.Message(content_data) 143 | speech_result = asr.run(message).content['result'][0] 144 | print('语音识别结果:', speech_result) 145 | return speech_result -------------------------------------------------------------------------------- /agent_demo_20240523/utils_vlm.py: -------------------------------------------------------------------------------- 1 | # utils_vlm.py 2 | # 同济子豪兄 2024-5-22 3 | # 多模态大模型、可视化 4 | 5 | print('导入视觉大模型模块') 6 | import cv2 7 | import numpy as np 8 | from PIL import Image 9 | from PIL import ImageFont, ImageDraw 10 | # 导入中文字体,指定字号 11 | font = ImageFont.truetype('asset/SimHei.ttf', 26) 12 | 13 | # 系统提示词 14 | SYSTEM_PROMPT = ''' 15 | 我即将说一句给机械臂的指令,你帮我从这句话中提取出起始物体和终止物体,并从这张图中分别找到这两个物体左上角和右下角的像素坐标,输出json数据结构。 16 | 17 | 例如,如果我的指令是:请帮我把红色方块放在房子简笔画上。 18 | 你输出这样的格式: 19 | { 20 | "start":"红色方块", 21 | "start_xyxy":[[102,505],[324,860]], 22 | "end":"房子简笔画", 23 | "end_xyxy":[[300,150],[476,310]] 24 | } 25 | 26 | 只回复json本身即可,不要回复其它内容 27 | 28 | 我现在的指令是: 29 | ''' 30 | 31 | # Yi-Vision调用函数 32 | import openai 33 | from openai import OpenAI 34 | import base64 35 | def yi_vision_api(PROMPT='帮我把红色方块放在钢笔上', img_path='temp/vl_now.jpg'): 36 | 37 | ''' 38 | 零一万物大模型开放平台,yi-vision视觉语言多模态大模型API 39 | ''' 40 | 41 | API_BASE = "https://api.lingyiwanwu.com/v1" 42 | API_KEY = "f8144ffaff7c4597910bb5d3a0c8440c" 43 | client = OpenAI( 44 | api_key=API_KEY, 45 | base_url=API_BASE 46 | ) 47 | 48 | # 编码为base64数据 49 | with open(img_path, 'rb') as image_file: 50 | image = 'data:image/jpeg;base64,' + base64.b64encode(image_file.read()).decode('utf-8') 51 | 52 | # 向大模型发起请求 53 | completion = client.chat.completions.create( 54 | model="yi-vision", 55 | messages=[ 56 | { 57 | "role": "user", 58 | "content": [ 59 | { 60 | "type": "text", 61 | "text": SYSTEM_PROMPT + PROMPT 62 | }, 63 | { 64 | "type": "image_url", 65 | "image_url": { 66 | "url": image 67 | } 68 | } 69 | ] 70 | }, 71 | ] 72 | ) 73 | 74 | # 解析大模型返回结果 75 | result = eval(completion.choices[0].message.content.strip()) 76 | print(' 大模型调用成功!') 77 | 78 | return result 79 | 80 | def post_processing_viz(result, img_path, check=False): 81 | 82 | ''' 83 | 视觉大模型输出结果后处理和可视化 84 | check:是否需要人工看屏幕确认可视化成功,按键继续或退出 85 | ''' 86 | 87 | # 后处理 88 | img_bgr = cv2.imread(img_path) 89 | img_h = img_bgr.shape[0] 90 | img_w = img_bgr.shape[1] 91 | # 缩放因子 92 | FACTOR = 999 93 | # 起点物体名称 94 | START_NAME = result['start'] 95 | # 终点物体名称 96 | END_NAME = result['end'] 97 | # 起点,左上角像素坐标 98 | START_X_MIN = int(result['start_xyxy'][0][0] * img_w / FACTOR) 99 | START_Y_MIN = int(result['start_xyxy'][0][1] * img_h / FACTOR) 100 | # 起点,右下角像素坐标 101 | START_X_MAX = int(result['start_xyxy'][1][0] * img_w / FACTOR) 102 | START_Y_MAX = int(result['start_xyxy'][1][1] * img_h / FACTOR) 103 | # 起点,中心点像素坐标 104 | START_X_CENTER = int((START_X_MIN + START_X_MAX) / 2) 105 | START_Y_CENTER = int((START_Y_MIN + START_Y_MAX) / 2) 106 | # 终点,左上角像素坐标 107 | END_X_MIN = int(result['end_xyxy'][0][0] * img_w / FACTOR) 108 | END_Y_MIN = int(result['end_xyxy'][0][1] * img_h / FACTOR) 109 | # 终点,右下角像素坐标 110 | END_X_MAX = int(result['end_xyxy'][1][0] * img_w / FACTOR) 111 | END_Y_MAX = int(result['end_xyxy'][1][1] * img_h / FACTOR) 112 | # 终点,中心点像素坐标 113 | END_X_CENTER = int((END_X_MIN + END_X_MAX) / 2) 114 | END_Y_CENTER = int((END_Y_MIN + END_Y_MAX) / 2) 115 | 116 | # 可视化 117 | # 画起点物体框 118 | img_bgr = cv2.rectangle(img_bgr, (START_X_MIN, START_Y_MIN), (START_X_MAX, START_Y_MAX), [0, 0, 255], thickness=3) 119 | # 画起点中心点 120 | img_bgr = cv2.circle(img_bgr, [START_X_CENTER, START_Y_CENTER], 6, [0, 0, 255], thickness=-1) 121 | # 画终点物体框 122 | img_bgr = cv2.rectangle(img_bgr, (END_X_MIN, END_Y_MIN), (END_X_MAX, END_Y_MAX), [255, 0, 0], thickness=3) 123 | # 画终点中心点 124 | img_bgr = cv2.circle(img_bgr, [END_X_CENTER, END_Y_CENTER], 6, [255, 0, 0], thickness=-1) 125 | # 写中文物体名称 126 | img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # BGR 转 RGB 127 | img_pil = Image.fromarray(img_rgb) # array 转 pil 128 | draw = ImageDraw.Draw(img_pil) 129 | # 写起点物体中文名称 130 | draw.text((START_X_MIN, START_Y_MIN-32), START_NAME, font=font, fill=(255, 0, 0, 1)) # 文字坐标,中文字符串,字体,rgba颜色 131 | # 写终点物体中文名称 132 | draw.text((END_X_MIN, END_Y_MIN-32), END_NAME, font=font, fill=(0, 0, 255, 1)) # 文字坐标,中文字符串,字体,rgba颜色 133 | img_bgr = cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR) # RGB转BGR 134 | print(' 展示可视化效果图,按c键继续,按q键退出') 135 | # 保存可视化效果图 136 | cv2.imwrite('temp/vl_now_viz.jpg', img_bgr) 137 | 138 | # 在屏幕上展示可视化效果图 139 | cv2.imshow('zihao_vlm', img_bgr) 140 | 141 | if check: 142 | print(' 请确认可视化成功,按c键继续,按q键退出') 143 | while(True): 144 | key = cv2.waitKey(10) & 0xFF 145 | if key == ord('c'): # 按c键继续 146 | break 147 | if key == ord('q'): # 按q键退出 148 | exit() 149 | else: 150 | if cv2.waitKey(1) & 0xFF == None: 151 | pass 152 | 153 | return START_X_CENTER, START_Y_CENTER, END_X_CENTER, END_Y_CENTER -------------------------------------------------------------------------------- /agent_demo_20240523/drag_trial_teaching.py: -------------------------------------------------------------------------------- 1 | import time 2 | import os 3 | import sys 4 | import termios 5 | import tty 6 | import threading 7 | import json 8 | import serial 9 | import serial.tools.list_ports 10 | 11 | from pymycobot.mycobot import MyCobot 12 | from pymycobot import PI_PORT, PI_BAUD 13 | 14 | # 连接机械臂 15 | mc = MyCobot(PI_PORT, PI_BAUD, debug=False) 16 | 17 | 18 | port: str 19 | mc: MyCobot 20 | sp: int = 80 21 | 22 | 23 | class Raw(object): 24 | """Set raw input mode for device""" 25 | 26 | def __init__(self, stream): 27 | self.stream = stream 28 | self.fd = self.stream.fileno() 29 | 30 | def __enter__(self): 31 | self.original_stty = termios.tcgetattr(self.stream) 32 | tty.setcbreak(self.stream) 33 | 34 | def __exit__(self, type, value, traceback): 35 | termios.tcsetattr(self.stream, termios.TCSANOW, self.original_stty) 36 | 37 | 38 | class Helper(object): 39 | def __init__(self) -> None: 40 | self.w, self.h = os.get_terminal_size() 41 | 42 | def echo(self, msg): 43 | print("\r{}".format(" " * self.w), end="") 44 | print("\r{}".format(msg), end="") 45 | 46 | 47 | class TeachingTest(Helper): 48 | def __init__(self, mycobot) -> None: 49 | super().__init__() 50 | self.mc = mycobot 51 | self.recording = False 52 | self.playing = False 53 | self.record_list = [] 54 | self.record_t = None 55 | self.play_t = None 56 | 57 | def record(self): 58 | self.record_list = [] 59 | self.recording = True 60 | self.mc.set_fresh_mode(0) 61 | def _record(): 62 | start_t = time.time() 63 | 64 | while self.recording: 65 | angles = self.mc.get_encoders() 66 | if angles: 67 | self.record_list.append(angles) 68 | time.sleep(0.1) 69 | print("\r {}".format(time.time() - start_t), end="") 70 | 71 | self.echo("Start recording.") 72 | self.record_t = threading.Thread(target=_record, daemon=True) 73 | self.record_t.start() 74 | 75 | def stop_record(self): 76 | if self.recording: 77 | self.recording = False 78 | self.record_t.join() 79 | self.echo("Stop record") 80 | 81 | def play(self): 82 | self.echo("Start play") 83 | for angles in self.record_list: 84 | # print(angles) 85 | self.mc.set_encoders(angles, 80) 86 | time.sleep(0.1) 87 | self.echo("Finish play") 88 | 89 | def loop_play(self): 90 | self.playing = True 91 | 92 | def _loop(): 93 | len_ = len(self.record_list) 94 | i = 0 95 | while self.playing: 96 | idx_ = i % len_ 97 | i += 1 98 | self.mc.set_encoders(self.record_list[idx_], 80) 99 | time.sleep(0.1) 100 | 101 | self.echo("Start loop play.") 102 | self.play_t = threading.Thread(target=_loop, daemon=True) 103 | self.play_t.start() 104 | 105 | def stop_loop_play(self): 106 | if self.playing: 107 | self.playing = False 108 | self.play_t.join() 109 | self.echo("Stop loop play.") 110 | 111 | def save_to_local(self): 112 | if not self.record_list: 113 | self.echo("No data should save.") 114 | return 115 | 116 | with open(os.path.dirname(__file__) + "/record.txt", "w") as f: 117 | json.dump(self.record_list, f, indent=2) 118 | self.echo("save dir: {}".format(os.path.dirname(__file__))) 119 | 120 | def load_from_local(self): 121 | 122 | with open(os.path.dirname(__file__) + "/record.txt", "r") as f: 123 | try: 124 | data = json.load(f) 125 | self.record_list = data 126 | self.echo("Load data success.") 127 | except Exception: 128 | self.echo("Error: invalid data.") 129 | 130 | def print_menu(self): 131 | print( 132 | """\ 133 | \r 拖动示教 同济子豪兄 134 | \r q: 退出 135 | \r r: 开始录制动作 136 | \r c: 停止录制动作 137 | \r p: 回放动作 138 | \r P: 循环回放/停止循环回放 139 | \r s: 将录制的动作保存到本地 140 | \r l: 从本地读取录制好的动作 141 | \r f: 放松机械臂 142 | \r---------------------------------- 143 | """ 144 | ) 145 | 146 | def start(self): 147 | self.print_menu() 148 | 149 | while not False: 150 | with Raw(sys.stdin): 151 | key = sys.stdin.read(1) 152 | if key == "q": 153 | break 154 | elif key == "r": # recorder 155 | self.record() 156 | elif key == "c": # stop recorder 157 | self.stop_record() 158 | elif key == "p": # play 159 | self.play() 160 | elif key == "P": # loop play 161 | if not self.playing: 162 | self.loop_play() 163 | else: 164 | self.stop_loop_play() 165 | elif key == "s": # save to local 166 | self.save_to_local() 167 | elif key == "l": # load from local 168 | self.load_from_local() 169 | elif key == "f": # free move 170 | self.mc.release_all_servos() 171 | self.echo("放松机械臂) 172 | else: 173 | print(key) 174 | continue 175 | 176 | 177 | if __name__ == "__main__": 178 | recorder = TeachingTest(mc) 179 | recorder.start() 180 | -------------------------------------------------------------------------------- /agent_demo_20240523/utils_drag_teaching.py: -------------------------------------------------------------------------------- 1 | # utils_drag_teaching.py 2 | # 同济子豪兄 2024-5-23 3 | # 拖动示教 4 | 5 | print('导入拖动示教模块') 6 | 7 | import time 8 | import os 9 | import sys 10 | import termios 11 | import tty 12 | import threading 13 | import json 14 | 15 | from pymycobot.mycobot import MyCobot 16 | from pymycobot import PI_PORT, PI_BAUD 17 | 18 | # 连接机械臂 19 | mc = MyCobot(PI_PORT, PI_BAUD, debug=False) 20 | 21 | class Raw(object): 22 | """Set raw input mode for device""" 23 | 24 | def __init__(self, stream): 25 | self.stream = stream 26 | self.fd = self.stream.fileno() 27 | 28 | def __enter__(self): 29 | self.original_stty = termios.tcgetattr(self.stream) 30 | tty.setcbreak(self.stream) 31 | 32 | def __exit__(self, type, value, traceback): 33 | termios.tcsetattr(self.stream, termios.TCSANOW, self.original_stty) 34 | 35 | 36 | class Helper(object): 37 | def __init__(self) -> None: 38 | self.w, self.h = os.get_terminal_size() 39 | 40 | def echo(self, msg): 41 | print("\r{}".format(" " * self.w), end="") 42 | print("\r{}".format(msg), end="") 43 | 44 | 45 | class TeachingTest(Helper): 46 | def __init__(self, mycobot) -> None: 47 | super().__init__() 48 | self.mc = mycobot 49 | self.recording = False 50 | self.playing = False 51 | self.record_list = [] 52 | self.record_t = None 53 | self.play_t = None 54 | 55 | def record(self): 56 | self.record_list = [] 57 | self.recording = True 58 | self.mc.set_fresh_mode(0) 59 | def _record(): 60 | start_t = time.time() 61 | 62 | while self.recording: 63 | angles = self.mc.get_encoders() 64 | if angles: 65 | self.record_list.append(angles) 66 | time.sleep(0.1) 67 | print("\r {}".format(time.time() - start_t), end="") 68 | 69 | self.echo("开始录制动作") 70 | self.record_t = threading.Thread(target=_record, daemon=True) 71 | self.record_t.start() 72 | 73 | def stop_record(self): 74 | if self.recording: 75 | self.recording = False 76 | self.record_t.join() 77 | self.echo("停止录制动作") 78 | 79 | def play(self): 80 | self.echo("开始回放动作") 81 | for angles in self.record_list: 82 | # print(angles) 83 | self.mc.set_encoders(angles, 80) 84 | time.sleep(0.1) 85 | self.echo("回放结束\n") 86 | 87 | def loop_play(self): 88 | self.playing = True 89 | 90 | def _loop(): 91 | len_ = len(self.record_list) 92 | i = 0 93 | while self.playing: 94 | idx_ = i % len_ 95 | i += 1 96 | self.mc.set_encoders(self.record_list[idx_], 80) 97 | time.sleep(0.1) 98 | 99 | self.echo("开始循环回放") 100 | self.play_t = threading.Thread(target=_loop, daemon=True) 101 | self.play_t.start() 102 | 103 | def stop_loop_play(self): 104 | if self.playing: 105 | self.playing = False 106 | self.play_t.join() 107 | self.echo("停止循环回放") 108 | 109 | def save_to_local(self): 110 | if not self.record_list: 111 | self.echo("No data should save.") 112 | return 113 | 114 | save_path = os.path.dirname(__file__) + "/temp/record.txt" 115 | with open(save_path, "w") as f: 116 | json.dump(self.record_list, f, indent=2) 117 | self.echo("回放动作导出至: {}".format(save_path)) 118 | 119 | def load_from_local(self): 120 | 121 | with open(os.path.dirname(__file__) + "/temp/record.txt", "r") as f: 122 | try: 123 | data = json.load(f) 124 | self.record_list = data 125 | self.echo("载入本地动作数据成功") 126 | except Exception: 127 | self.echo("Error: invalid data.") 128 | 129 | def print_menu(self): 130 | print( 131 | """\ 132 | \r 拖动示教 同济子豪兄 133 | \r q: 退出 134 | \r r: 开始录制动作 135 | \r c: 停止录制动作 136 | \r p: 回放动作 137 | \r P: 循环回放/停止循环回放 138 | \r s: 将录制的动作保存到本地 139 | \r l: 从本地读取录制好的动作 140 | \r f: 放松机械臂 141 | \r---------------------------------- 142 | """ 143 | ) 144 | 145 | def start(self): 146 | self.print_menu() 147 | 148 | while not False: 149 | with Raw(sys.stdin): 150 | key = sys.stdin.read(1) 151 | if key == "q": 152 | break 153 | elif key == "r": # recorder 154 | self.record() 155 | elif key == "c": # stop recorder 156 | self.stop_record() 157 | elif key == "p": # play 158 | self.play() 159 | elif key == "P": # loop play 160 | if not self.playing: 161 | self.loop_play() 162 | else: 163 | self.stop_loop_play() 164 | elif key == "s": # save to local 165 | self.save_to_local() 166 | elif key == "l": # load from local 167 | self.load_from_local() 168 | elif key == "f": # free move 169 | self.mc.release_all_servos() 170 | self.echo("Released") 171 | else: 172 | print(key) 173 | continue 174 | 175 | def drag_teach(): 176 | 177 | print('机械臂归零') 178 | mc.send_angles([0, 0, 0, 0, 0, 0], 40) 179 | time.sleep(3) 180 | 181 | recorder = TeachingTest(mc) 182 | recorder.start() 183 | 184 | print('机械臂归零') 185 | mc.send_angles([0, 0, 0, 0, 0, 0], 40) 186 | time.sleep(3) 187 | -------------------------------------------------------------------------------- /agent_demo_20240523/utils_robot.py: -------------------------------------------------------------------------------- 1 | # utils_robot.py 2 | # 同济子豪兄 2024-5-22 3 | # 启动并连接机械臂,导入各种工具包 4 | 5 | print('导入机械臂连接模块') 6 | 7 | from pymycobot.mycobot import MyCobot 8 | from pymycobot import PI_PORT, PI_BAUD 9 | import cv2 10 | import numpy as np 11 | import time 12 | from utils_pump import * 13 | 14 | # 连接机械臂 15 | mc = MyCobot(PI_PORT, PI_BAUD) 16 | # 设置运动模式为插补 17 | mc.set_fresh_mode(0) 18 | 19 | import RPi.GPIO as GPIO 20 | # 初始化GPIO 21 | GPIO.setwarnings(False) # 不打印 warning 信息 22 | GPIO.setmode(GPIO.BCM) 23 | GPIO.setup(20, GPIO.OUT) 24 | GPIO.setup(21, GPIO.OUT) 25 | GPIO.output(20, 1) # 关闭吸泵电磁阀 26 | 27 | def back_zero(): 28 | ''' 29 | 机械臂归零 30 | ''' 31 | print('机械臂归零') 32 | mc.send_angles([0, 0, 0, 0, 0, 0], 40) 33 | time.sleep(3) 34 | 35 | def relax_arms(): 36 | print('放松机械臂关节') 37 | mc.release_all_servos() 38 | 39 | def head_shake(): 40 | # 左右摆头 41 | mc.send_angles([0.87,(-50.44),47.28,0.35,(-0.43),(-0.26)],70) 42 | time.sleep(1) 43 | for count in range(2): 44 | mc.send_angle(5, 30, 80) 45 | time.sleep(0.5) 46 | mc.send_angle(5, -30,80) 47 | time.sleep(0.5) 48 | # mc.send_angles([0.87,(-50.44),47.28,0.35,(-0.43),(-0.26)],70) 49 | # time.sleep(1) 50 | mc.send_angles([0, 0, 0, 0, 0, 0], 40) 51 | time.sleep(2) 52 | 53 | def head_dance(): 54 | # 跳舞 55 | mc.send_angles([0.87,(-50.44),47.28,0.35,(-0.43),(-0.26)],70) 56 | time.sleep(1) 57 | for count in range(2): 58 | mc.send_angles([(-0.17),(-94.3),118.91,(-39.9),59.32,(-0.52)],80) 59 | time.sleep(1.2) 60 | mc.send_angles([67.85,(-3.42),(-116.98),106.52,23.11,(-0.52)],80) 61 | time.sleep(1.7) 62 | mc.send_angles([(-38.14),(-115.04),116.63,69.69,3.25,(-11.6)],80) 63 | time.sleep(1.7) 64 | mc.send_angles([2.72,(-26.19),140.27,(-110.74),(-6.15),(-11.25)],80) 65 | time.sleep(1) 66 | mc.send_angles([0,0,0,0,0,0],80) 67 | 68 | def head_nod(): 69 | # 点头 70 | mc.send_angles([0.87,(-50.44),47.28,0.35,(-0.43),(-0.26)],70) 71 | for count in range(2): 72 | mc.send_angle(4, 13, 70) 73 | time.sleep(0.5) 74 | mc.send_angle(4, -20, 70) 75 | time.sleep(1) 76 | mc.send_angle(4,13,70) 77 | time.sleep(0.5) 78 | mc.send_angles([0.87,(-50.44),47.28,0.35,(-0.43),(-0.26)],70) 79 | 80 | def move_to_coords(X=150, Y=-130, HEIGHT_SAFE=230): 81 | print('移动至指定坐标:X {} Y {}'.format(X, Y)) 82 | mc.send_coords([X, Y, HEIGHT_SAFE, 0, 180, 90], 20, 0) 83 | time.sleep(4) 84 | 85 | def single_joint_move(joint_index, angle): 86 | print('关节 {} 旋转至 {} 度'.format(joint_index, angle)) 87 | mc.send_angle(joint_index, angle, 40) 88 | time.sleep(2) 89 | 90 | def move_to_top_view(): 91 | print('移动至俯视姿态') 92 | mc.send_angles([-62.13, 8.96, -87.71, -14.41, 2.54, -16.34], 10) 93 | time.sleep(3) 94 | 95 | def top_view_shot(check=False): 96 | ''' 97 | 拍摄一张图片并保存 98 | check:是否需要人工看屏幕确认拍照成功,再在键盘上按q键确认继续 99 | ''' 100 | print(' 移动至俯视姿态') 101 | move_to_top_view() 102 | 103 | # 获取摄像头,传入0表示获取系统默认摄像头 104 | cap = cv2.VideoCapture(0) 105 | # 打开cap 106 | cap.open(0) 107 | time.sleep(0.3) 108 | success, img_bgr = cap.read() 109 | 110 | # 保存图像 111 | print(' 保存至temp/vl_now.jpg') 112 | cv2.imwrite('temp/vl_now.jpg', img_bgr) 113 | 114 | # 屏幕上展示图像 115 | cv2.destroyAllWindows() # 关闭所有opencv窗口 116 | cv2.imshow('zihao_vlm', img_bgr) 117 | 118 | if check: 119 | print('请确认拍照成功,按c键继续,按q键退出') 120 | while(True): 121 | key = cv2.waitKey(10) & 0xFF 122 | if key == ord('c'): # 按c键继续 123 | break 124 | if key == ord('q'): # 按q键退出 125 | exit() 126 | else: 127 | if cv2.waitKey(10) & 0xFF == None: 128 | pass 129 | 130 | # 关闭摄像头 131 | cap.release() 132 | # 关闭图像窗口 133 | # cv2.destroyAllWindows() 134 | 135 | def eye2hand(X_im=160, Y_im=120): 136 | ''' 137 | 输入目标点在图像中的像素坐标,转换为机械臂坐标 138 | ''' 139 | 140 | # 整理两个标定点的坐标 141 | cali_1_im = [130, 290] # 左下角,第一个标定点的像素坐标,要手动填! 142 | cali_1_mc = [-21.8, -197.4] # 左下角,第一个标定点的机械臂坐标,要手动填! 143 | cali_2_im = [640, 0] # 右上角,第二个标定点的像素坐标 144 | cali_2_mc = [215, -59.1] # 右上角,第二个标定点的机械臂坐标,要手动填! 145 | 146 | X_cali_im = [cali_1_im[0], cali_2_im[0]] # 像素坐标 147 | X_cali_mc = [cali_1_mc[0], cali_2_mc[0]] # 机械臂坐标 148 | Y_cali_im = [cali_2_im[1], cali_1_im[1]] # 像素坐标,先小后大 149 | Y_cali_mc = [cali_2_mc[1], cali_1_mc[1]] # 机械臂坐标,先大后小 150 | 151 | # X差值 152 | X_mc = int(np.interp(X_im, X_cali_im, X_cali_mc)) 153 | 154 | # Y差值 155 | Y_mc = int(np.interp(Y_im, Y_cali_im, Y_cali_mc)) 156 | 157 | return X_mc, Y_mc 158 | 159 | # 吸泵吸取并移动物体 160 | def pump_move(mc, XY_START=[230,-50], HEIGHT_START=90, XY_END=[100,220], HEIGHT_END=100, HEIGHT_SAFE=220): 161 | 162 | ''' 163 | 用吸泵,将物体从起点吸取移动至终点 164 | 165 | mc:机械臂实例 166 | XY_START:起点机械臂坐标 167 | HEIGHT_START:起点高度 168 | XY_END:终点机械臂坐标 169 | HEIGHT_END:终点高度 170 | HEIGHT_SAFE:搬运途中安全高度 171 | ''' 172 | 173 | # 初始化GPIO 174 | GPIO.setmode(GPIO.BCM) 175 | GPIO.setup(20, GPIO.OUT) 176 | GPIO.setup(21, GPIO.OUT) 177 | 178 | # 设置运动模式为插补 179 | mc.set_fresh_mode(0) 180 | 181 | # # 机械臂归零 182 | # print(' 机械臂归零') 183 | # mc.send_angles([0, 0, 0, 0, 0, 0], 40) 184 | # time.sleep(4) 185 | 186 | # 吸泵移动至物体上方 187 | print(' 吸泵移动至物体上方') 188 | mc.send_coords([XY_START[0], XY_START[1], HEIGHT_SAFE, 0, 180, 90], 20, 0) 189 | time.sleep(4) 190 | 191 | # 开启吸泵 192 | pump_on() 193 | 194 | # 吸泵向下吸取物体 195 | print(' 吸泵向下吸取物体') 196 | mc.send_coords([XY_START[0], XY_START[1], HEIGHT_START, 0, 180, 90], 15, 0) 197 | time.sleep(4) 198 | 199 | # 升起物体 200 | print(' 升起物体') 201 | mc.send_coords([XY_START[0], XY_START[1], HEIGHT_SAFE, 0, 180, 90], 15, 0) 202 | time.sleep(4) 203 | 204 | # 搬运物体至目标上方 205 | print(' 搬运物体至目标上方') 206 | mc.send_coords([XY_END[0], XY_END[1], HEIGHT_SAFE, 0, 180, 90], 15, 0) 207 | time.sleep(4) 208 | 209 | # 向下放下物体 210 | print(' 向下放下物体') 211 | mc.send_coords([XY_END[0], XY_END[1], HEIGHT_END, 0, 180, 90], 20, 0) 212 | time.sleep(3) 213 | 214 | # 关闭吸泵 215 | pump_off() 216 | 217 | # 机械臂归零 218 | print(' 机械臂归零') 219 | mc.send_angles([0, 0, 0, 0, 0, 0], 40) 220 | time.sleep(3) -------------------------------------------------------------------------------- /agent_demo_20240523/temp/record.txt: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | 2158, 4 | 2046, 5 | 1786, 6 | 1747, 7 | 1807, 8 | 1326 9 | ], 10 | [ 11 | 2158, 12 | 2046, 13 | 1786, 14 | 1744, 15 | 1808, 16 | 1326 17 | ], 18 | [ 19 | 2158, 20 | 2047, 21 | 1786, 22 | 1743, 23 | 1808, 24 | 1326 25 | ], 26 | [ 27 | 2158, 28 | 2051, 29 | 1781, 30 | 1730, 31 | 1808, 32 | 1326 33 | ], 34 | [ 35 | 2158, 36 | 2051, 37 | 1780, 38 | 1711, 39 | 1808, 40 | 1326 41 | ], 42 | [ 43 | 2158, 44 | 2061, 45 | 1780, 46 | 1683, 47 | 1808, 48 | 1326 49 | ], 50 | [ 51 | 2158, 52 | 2104, 53 | 1780, 54 | 1623, 55 | 1808, 56 | 1326 57 | ], 58 | [ 59 | 2158, 60 | 2153, 61 | 1780, 62 | 1562, 63 | 1816, 64 | 1326 65 | ], 66 | [ 67 | 2158, 68 | 2185, 69 | 1780, 70 | 1522, 71 | 1819, 72 | 1326 73 | ], 74 | [ 75 | 2158, 76 | 2221, 77 | 1780, 78 | 1492, 79 | 1820, 80 | 1326 81 | ], 82 | [ 83 | 2159, 84 | 2269, 85 | 1780, 86 | 1454, 87 | 1820, 88 | 1326 89 | ], 90 | [ 91 | 2159, 92 | 2324, 93 | 1780, 94 | 1406, 95 | 1820, 96 | 1326 97 | ], 98 | [ 99 | 2160, 100 | 2382, 101 | 1780, 102 | 1358, 103 | 1820, 104 | 1326 105 | ], 106 | [ 107 | 2160, 108 | 2442, 109 | 1782, 110 | 1309, 111 | 1819, 112 | 1326 113 | ], 114 | [ 115 | 2162, 116 | 2488, 117 | 1780, 118 | 1251, 119 | 1817, 120 | 1326 121 | ], 122 | [ 123 | 2160, 124 | 2538, 125 | 1780, 126 | 1204, 127 | 1814, 128 | 1326 129 | ], 130 | [ 131 | 2159, 132 | 2596, 133 | 1784, 134 | 1176, 135 | 1808, 136 | 1326 137 | ], 138 | [ 139 | 2163, 140 | 2638, 141 | 1780, 142 | 1128, 143 | 1814, 144 | 1324 145 | ], 146 | [ 147 | 2160, 148 | 2718, 149 | 1780, 150 | 1044, 151 | 1855, 152 | 1300 153 | ], 154 | [ 155 | 2159, 156 | 2781, 157 | 1780, 158 | 990, 159 | 1926, 160 | 1251 161 | ], 162 | [ 163 | 2162, 164 | 2821, 165 | 1782, 166 | 989, 167 | 1958, 168 | 1176 169 | ], 170 | [ 171 | 2163, 172 | 2844, 173 | 1780, 174 | 992, 175 | 1970, 176 | 1131 177 | ], 178 | [ 179 | 2163, 180 | 2874, 181 | 1783, 182 | 979, 183 | 1983, 184 | 1097 185 | ], 186 | [ 187 | 2163, 188 | 2904, 189 | 1784, 190 | 941, 191 | 2010, 192 | 1061 193 | ], 194 | [ 195 | 2163, 196 | 2912, 197 | 1785, 198 | 932, 199 | 2023, 200 | 1055 201 | ], 202 | [ 203 | 2163, 204 | 2913, 205 | 1808, 206 | 948, 207 | 2025, 208 | 1055 209 | ], 210 | [ 211 | 2163, 212 | 2936, 213 | 1901, 214 | 1038, 215 | 2025, 216 | 1055 217 | ], 218 | [ 219 | 2163, 220 | 2988, 221 | 2051, 222 | 1131, 223 | 2028, 224 | 1055 225 | ], 226 | [ 227 | 2164, 228 | 3039, 229 | 2210, 230 | 1247, 231 | 2030, 232 | 1055 233 | ], 234 | [ 235 | 2188, 236 | 3076, 237 | 2352, 238 | 1365, 239 | 2022, 240 | 1051 241 | ], 242 | [ 243 | 2229, 244 | 3100, 245 | 2468, 246 | 1467, 247 | 1990, 248 | 1030 249 | ], 250 | [ 251 | 2253, 252 | 3118, 253 | 2559, 254 | 1543, 255 | 1979, 256 | 1007 257 | ], 258 | [ 259 | 2260, 260 | 3139, 261 | 2632, 262 | 1584, 263 | 1980, 264 | 1006 265 | ], 266 | [ 267 | 2260, 268 | 3162, 269 | 2705, 270 | 1613, 271 | 1985, 272 | 1006 273 | ], 274 | [ 275 | 2273, 276 | 3189, 277 | 2786, 278 | 1658, 279 | 1990, 280 | 1006 281 | ], 282 | [ 283 | 2313, 284 | 3215, 285 | 2868, 286 | 1715, 287 | 1969, 288 | 1006 289 | ], 290 | [ 291 | 2362, 292 | 3233, 293 | 2949, 294 | 1786, 295 | 1953, 296 | 1006 297 | ], 298 | [ 299 | 2422, 300 | 3247, 301 | 3025, 302 | 1845, 303 | 1922, 304 | 1006 305 | ], 306 | [ 307 | 2473, 308 | 3265, 309 | 3095, 310 | 1868, 311 | 1876, 312 | 1006 313 | ], 314 | [ 315 | 2516, 316 | 3293, 317 | 3168, 318 | 1874, 319 | 1855, 320 | 1006 321 | ], 322 | [ 323 | 2552, 324 | 3332, 325 | 3252, 326 | 1902, 327 | 1842, 328 | 1006 329 | ], 330 | [ 331 | 2591, 332 | 3372, 333 | 3335, 334 | 1938, 335 | 1842, 336 | 1006 337 | ], 338 | [ 339 | 2635, 340 | 3397, 341 | 3389, 342 | 1969, 343 | 1912, 344 | 1006 345 | ], 346 | [ 347 | 2694, 348 | 3401, 349 | 3401, 350 | 2025, 351 | 2010, 352 | 1006 353 | ], 354 | [ 355 | 2756, 356 | 3400, 357 | 3401, 358 | 2051, 359 | 2000, 360 | 1006 361 | ], 362 | [ 363 | 2808, 364 | 3397, 365 | 3401, 366 | 2054, 367 | 2011, 368 | 1006 369 | ], 370 | [ 371 | 2855, 372 | 3396, 373 | 3401, 374 | 2039, 375 | 2044, 376 | 1006 377 | ], 378 | [ 379 | 2896, 380 | 3396, 381 | 3401, 382 | 2003, 383 | 2062, 384 | 1006 385 | ], 386 | [ 387 | 2930, 388 | 3396, 389 | 3401, 390 | 1982, 391 | 2090, 392 | 1006 393 | ], 394 | [ 395 | 2971, 396 | 3396, 397 | 3401, 398 | 1980, 399 | 2109, 400 | 1006 401 | ], 402 | [ 403 | 3004, 404 | 3395, 405 | 3401, 406 | 1994, 407 | 2110, 408 | 1006 409 | ], 410 | [ 411 | 3005, 412 | 3355, 413 | 3398, 414 | 2067, 415 | 2110, 416 | 1006 417 | ], 418 | [ 419 | 3004, 420 | 3246, 421 | 3385, 422 | 2248, 423 | 2107, 424 | 1006 425 | ], 426 | [ 427 | 3004, 428 | 3118, 429 | 3320, 430 | 2402, 431 | 2103, 432 | 1006 433 | ], 434 | [ 435 | 2995, 436 | 3026, 437 | 3209, 438 | 2386, 439 | 2103, 440 | 1006 441 | ], 442 | [ 443 | 2979, 444 | 2953, 445 | 3081, 446 | 2351, 447 | 2104, 448 | 999 449 | ], 450 | [ 451 | 2978, 452 | 2889, 453 | 2925, 454 | 2312, 455 | 2077, 456 | 999 457 | ], 458 | [ 459 | 2962, 460 | 2835, 461 | 2746, 462 | 2207, 463 | 2053, 464 | 999 465 | ], 466 | [ 467 | 2939, 468 | 2774, 469 | 2557, 470 | 2037, 471 | 2006, 472 | 999 473 | ], 474 | [ 475 | 2904, 476 | 2716, 477 | 2372, 478 | 1904, 479 | 1966, 480 | 999 481 | ], 482 | [ 483 | 2867, 484 | 2658, 485 | 2221, 486 | 1829, 487 | 1951, 488 | 999 489 | ], 490 | [ 491 | 2808, 492 | 2604, 493 | 2130, 494 | 1790, 495 | 1952, 496 | 999 497 | ], 498 | [ 499 | 2714, 500 | 2583, 501 | 2115, 502 | 1800, 503 | 2007, 504 | 999 505 | ], 506 | [ 507 | 2610, 508 | 2544, 509 | 2114, 510 | 1905, 511 | 2063, 512 | 993 513 | ], 514 | [ 515 | 2552, 516 | 2497, 517 | 2088, 518 | 2001, 519 | 2095, 520 | 894 521 | ], 522 | [ 523 | 2546, 524 | 2435, 525 | 1960, 526 | 1971, 527 | 2055, 528 | 802 529 | ], 530 | [ 531 | 2541, 532 | 2351, 533 | 1743, 534 | 1820, 535 | 2001, 536 | 802 537 | ], 538 | [ 539 | 2498, 540 | 2251, 541 | 1493, 542 | 1628, 543 | 1966, 544 | 802 545 | ], 546 | [ 547 | 2426, 548 | 2152, 549 | 1272, 550 | 1459, 551 | 1947, 552 | 802 553 | ], 554 | [ 555 | 2342, 556 | 2056, 557 | 1121, 558 | 1377, 559 | 1902, 560 | 805 561 | ], 562 | [ 563 | 2268, 564 | 1961, 565 | 1038, 566 | 1387, 567 | 1839, 568 | 829 569 | ], 570 | [ 571 | 2215, 572 | 1865, 573 | 1004, 574 | 1452, 575 | 1799, 576 | 855 577 | ], 578 | [ 579 | 2174, 580 | 1775, 581 | 984, 582 | 1531, 583 | 1756, 584 | 921 585 | ], 586 | [ 587 | 2122, 588 | 1702, 589 | 947, 590 | 1551, 591 | 1724, 592 | 999 593 | ], 594 | [ 595 | 2058, 596 | 1626, 597 | 881, 598 | 1542, 599 | 1714, 600 | 1020 601 | ], 602 | [ 603 | 2005, 604 | 1525, 605 | 783, 606 | 1516, 607 | 1717, 608 | 1023 609 | ], 610 | [ 611 | 1960, 612 | 1413, 613 | 675, 614 | 1483, 615 | 1723, 616 | 1038 617 | ], 618 | [ 619 | 1922, 620 | 1301, 621 | 576, 622 | 1469, 623 | 1719, 624 | 1089 625 | ], 626 | [ 627 | 1898, 628 | 1192, 629 | 498, 630 | 1477, 631 | 1704, 632 | 1139 633 | ], 634 | [ 635 | 1893, 636 | 1102, 637 | 438, 638 | 1493, 639 | 1681, 640 | 1146 641 | ], 642 | [ 643 | 1893, 644 | 1054, 645 | 399, 646 | 1497, 647 | 1662, 648 | 1146 649 | ], 650 | [ 651 | 1893, 652 | 1051, 653 | 393, 654 | 1482, 655 | 1657, 656 | 1146 657 | ], 658 | [ 659 | 1893, 660 | 1064, 661 | 395, 662 | 1422, 663 | 1656, 664 | 1145 665 | ], 666 | [ 667 | 1893, 668 | 1118, 669 | 398, 670 | 1343, 671 | 1647, 672 | 1145 673 | ], 674 | [ 675 | 1893, 676 | 1204, 677 | 410, 678 | 1242, 679 | 1649, 680 | 1139 681 | ], 682 | [ 683 | 1893, 684 | 1301, 685 | 480, 686 | 1214, 687 | 1661, 688 | 1127 689 | ], 690 | [ 691 | 1893, 692 | 1395, 693 | 603, 694 | 1250, 695 | 1685, 696 | 1124 697 | ], 698 | [ 699 | 1893, 700 | 1497, 701 | 755, 702 | 1317, 703 | 1717, 704 | 1111 705 | ], 706 | [ 707 | 1894, 708 | 1609, 709 | 918, 710 | 1377, 711 | 1757, 712 | 1107 713 | ], 714 | [ 715 | 1897, 716 | 1726, 717 | 1084, 718 | 1418, 719 | 1803, 720 | 1107 721 | ], 722 | [ 723 | 1897, 724 | 1840, 725 | 1252, 726 | 1464, 727 | 1850, 728 | 1107 729 | ], 730 | [ 731 | 1897, 732 | 1928, 733 | 1412, 734 | 1538, 735 | 1895, 736 | 1107 737 | ], 738 | [ 739 | 1897, 740 | 1989, 741 | 1559, 742 | 1636, 743 | 1939, 744 | 1107 745 | ], 746 | [ 747 | 1897, 748 | 2025, 749 | 1690, 750 | 1740, 751 | 1976, 752 | 1107 753 | ], 754 | [ 755 | 1898, 756 | 2050, 757 | 1799, 758 | 1839, 759 | 2003, 760 | 1107 761 | ], 762 | [ 763 | 1898, 764 | 2057, 765 | 1868, 766 | 1944, 767 | 2019, 768 | 1107 769 | ], 770 | [ 771 | 1898, 772 | 2053, 773 | 1887, 774 | 2032, 775 | 2055, 776 | 1107 777 | ], 778 | [ 779 | 1898, 780 | 2052, 781 | 1884, 782 | 2061, 783 | 2086, 784 | 1107 785 | ], 786 | [ 787 | 1898, 788 | 2056, 789 | 1883, 790 | 2126, 791 | 2135, 792 | 1107 793 | ], 794 | [ 795 | 1898, 796 | 2057, 797 | 1883, 798 | 2207, 799 | 2155, 800 | 1107 801 | ], 802 | [ 803 | 1898, 804 | 2053, 805 | 1886, 806 | 2218, 807 | 2152, 808 | 1107 809 | ], 810 | [ 811 | 1898, 812 | 2052, 813 | 1886, 814 | 2218, 815 | 2154, 816 | 1107 817 | ], 818 | [ 819 | 1898, 820 | 2050, 821 | 1887, 822 | 2175, 823 | 2147, 824 | 1107 825 | ], 826 | [ 827 | 1898, 828 | 2055, 829 | 1883, 830 | 2081, 831 | 2117, 832 | 1107 833 | ], 834 | [ 835 | 1897, 836 | 2057, 837 | 1882, 838 | 2072, 839 | 2107, 840 | 1107 841 | ] 842 | ] --------------------------------------------------------------------------------