При моделировании робототехнических систем в программном комплексе CoppeliaSim пользователь сталкивается с необходимостью создания настраиваемого пользовательского интерфейса (сustom user interfaces). CoppeliaSim предлагает создание настраиваемого пользовательского интерфейса с помощью штатного стредства – встроенного плагина Qt.
К сожалению плагин Qt ограничен в возможнастях. Однако стоит отметить, что данный недостаток вытекает из его основного достоинства – простоты разработки пользовательского интерфейса.
В рамках данной статьи рассмотрим процесс создания пользовательского интерфейса в Lazarus (бесплатный аналог Delphi). В качестве примера возмем модель промышленного робота ABB IRB 140.
Запустим CoppeliaSim и добавим модель робота ABB IRB 140 на сцену. Для этого в обзорщике моделей в дереве robots необходимо выбирать no-mobile и перетащить промышленного робота ABB IRB 140 на сцену. Модель промышленного робота ABB IRB 140 предс��авлена на рисунке 1.

Далее необходимо удалить ненужные объекты из иерархии сцены, как показано на рисунке 2.

Для всех шарниров (IRB140_joint1…IRB140_joint6) в окне Scene Object Properties во вкладке Joint в поле Mode значение Torque/force mode необходимо изменить на Passive mode.
Далее приступим к разработке управляющего скрипта.
Связь между программами будем осуществлять посредством TCP/IP протокола по 5050 сокету. В качестве клиента будет выступать приложение, созданное в Lazarus, в качестве сервера – CoppeliaSim.
Клиент на сервер будет передавать информацию в следующем виде:
[FK#J1=0.00,J2=0.00,J3=0.00,J4=0.00,J5=0.00,J6=0.00]
Наш управляющий скрипт для CoppeliaSim:
local socket = require("socket") local server = assert(socket.bind("*", 5050)) local tcp = assert(socket.tcp()) function sysCall_init() simJoints={} for i=1,6,1 do simJoints[i]=sim.getObjectHandle('IRB140_joint'..i) end end function sysCall_actuation() local client = server:accept() --print("request received") client:settimeout(10) local packet, err = client:receive() if not err then --print("received from client=",packet) cmd=string.sub(packet, 2,3) ----------------------------------------------- if string.find(cmd, "FK") then i= string.find(packet, "J1=") j= string.find(packet, ",J2=") j1=string.sub(packet, i+3, j-1) j1=string.gsub(j1, ",", ".") j1=tonumber(j1) ------------------------------- i= string.find(packet, "J2=") j= string.find(packet, ",J3=") j2=string.sub(packet, i+3, j-1) j2=string.gsub(j2, ",", ".") j2=tonumber(j2) ------------------------------- i= string.find(packet, "J3=") j= string.find(packet, ",J4=") j3=string.sub(packet, i+3, j-1) j3=string.gsub(j3, ",", ".") j3=tonumber(j3) ------------------------------- i= string.find(packet, "J4=") j= string.find(packet, ",J5=") j4=string.sub(packet, i+3, j-1) j4=string.gsub(j4, ",", ".") j4=tonumber(j4) ------------------------------- i= string.find(packet, "J5=") j= string.find(packet, ",J6=") j5=string.sub(packet, i+3, j-1) j5=string.gsub(j5, ",", ".") j5=tonumber(j5) ------------------------------- i= string.find(packet, "J6=") j= string.find(packet, "]") j6=string.sub(packet, i+3, j-1) j6=string.gsub(j6, ",", ".") j6=tonumber(j6) ------------------------------- --print("J1=",j1) --print("J2=",j2) --print("J3=",j3) --print("J4=",j4) --print("J5=",j5) --print("J6=",j6) ------------------------------- sim.setJointPosition(simJoints[1],j1) sim.setJointPosition(simJoints[2],j2) sim.setJointPosition(simJoints[3],j3) sim.setJointPosition(simJoints[4],j4) sim.setJointPosition(simJoints[5],j5) sim.setJointPosition(simJoints[6],j6) end client:send(packet .. "\n") end -- print("closing socket") client:close() end
С помощью string.gsub в принимаемой строке производим замену всех запятых на точки. Данное действие необходимо из-за русскоязычной локализации Windows.
Далее приступим к созданию клиента.
Узнаем максимальные значения углов поворота осей для промышленного робота ABB IRB 140 из документации на него (либо ищем в интернете). Не забываем, что на сервер мы отправляем значения углов в радианах.
Данная статья подразумевает некоторое умение работы с Delphi/Lazarus и знание их основных компонентов, поэтому опустим эти моменты. Подчеркнем лишь то, что для передачи на сервер мы формируем стоку в следующем виде:
[FK#J1=0.00,J2=0.00,J3=0.00,J4=0.00,J5=0.00,J6=0.00]
В разделе uses подключаем библиотеку WinSock.
В разделе var объявим следующие переменные:
S:TSocket; Addr:TSockAddr; Data:TWSAData; i: integer; b: byte; bfr: TBytes;
В обработчике нажатия кнопки «Старт» пишем следующий код:
procedure TForm1.Button1Click(Sender: TObject); begin WSAStartup($101, Data); //загружаем WinSock; Timer1.Enabled:=true; end;
Добавляем на форму компонент таймер TTimer и в обработчике событий таймера OnTimer пишем следующий код:
procedure TForm1.Timer1Timer(Sender: TObject); begin s:=Socket(AF_INET, SOCK_STREAM,IPPROTO_IP); //создаем сокет Addr.sin_family:=AF_Inet; //задаем семейство адресов Addr.sin_port:=HToNS(5050); //задаем номер порта Addr.sin_addr.S_addr:=Inet_Addr('127.0.0.1'); //задаем IP-адрес FillChar(Addr.Sin_Zero,SizeOf(Addr.Sin_Zero),0); //заполняем нулями поля Connect(S,Addr,SizeOf(TSockAddr)); //подключаемся к серверу st:='[FK#J1='+FloatToStr(j1)+',J2='+FloatToStr(j2)+',J3='+FloatToStr(j3)+',J4='+FloatToStr(j5)+',J5='+FloatToStr(j4)+',J6='+FloatToStr(j6)+']'; //передаваемая строка bfr:= SysUtils.TEncoding.ASCII.GetBytes(st + chr(13) + chr(10)); // переводим строку в массив байтов for i:=0 to ((Length(bfr))-1) do //отправляем данные на сервер по одному байту begin b:=bfr[i]; Send(S,b,1,0); end; Shutdown (s, 1); //завершение соединения CloseSocket(S); //отключение от сервера sleep(1); end;
В обработчике нажатия кнопки «Стоп» пишем следующий код:
procedure TForm1.Button2Click(Sender: TObject); begin Timer1.Enabled:=false; Shutdown (s, 1); //завершение соединения CloseSocket(S); //отключение от сервера WSACleanup(); //выгрузка сетевой библиотеки end;
В нашем случае приложение имеет вид, представленный на рисунке 3.

Исходный файл проекта располагается на сервисе GitHub по следующей ссылке.
Запускаем симулирование промышленного робота ABB IRB 140 в CoppeliaSim, нажимаем на кнопку «Старт» в окне нашего приложения созданного Lazarus и радуемся результатом – теперь моделью промышленного робота можно управлять.
Таким образом, применение внешней интегрированной среды разработки может значительно улучшить процесс моделирования в CoppeliaSim.
Литература
1. Визуальное представление прямой задачи кинематики промышленного робота ABB IRB 140 с помощью CoppeliaSim и Qt. — Текст : электронный // Habr : [сайт]. — URL: https://habr.com/ru/post/598987/ (дата обращения: 29.01.2022).
2. LuaSocket API documentation. — Текст : электронный // Defold : [сайт]. — URL: https://defold.com/ref/beta/socket/ (дата обращения: 29.01.2022).
3. Использование сокетов в Delphi. Часть первая: стандартные сокеты. — Текст : электронный // Королевство Delphi : [сайт]. — URL: http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1021 (дата обращения: 29.01.2022).
4. Фленов, М. Е. Delphi в шутку и всерьез: что умеют хакеры (+CD) / М. Е. Фленов. — СПб : Питер, 2006. — 271 c.
5. Lua 5.3 Руководство. — Текст : электронный // Lua : [сайт]. — URL: https://lua.org.ru/contents_ru.html (дата обращения: 29.01.2022).
6. String Library Tutorial. — Текст : электронный // lua-users wiki : [сайт]. — URL: http://lua-users.org/wiki/StringLibraryTutorial (дата обращения: 29.01.2022).
7. System.SysUtils.TEncoding.GetBytes.
— Текст : электронный // RAD Studio API Documentation : [сайт]. — URL:
https://docwiki.embarcadero.com/Libraries/Sydney/en/System.SysUtils.TEncoding.GetBytes
(дата обращения: 29.01.2022).
