这篇教程简要说明,在CoppeliaSim仿真环境中,使用ROS2接口,如果是ROS 2 Dashing,直接使用安装包中的compiledRosPlugins文件夹下的libsimExtROS2Interface.so。
如果使用其他版本的ROS 2,需要手动编译生成.so文件,否则会出错。当然也支持ROS 1参考之前博文。
重新编译ROS 2之CoppeliaSim插件的过程如下所示:
下载simExtROS2Interface,这个功能包在编译成功后会生成.so文件。
更多案例参考示例和controlTypeExamples下文件。
所有文件需要复制到如ros2_ws/src的文件夹下,然后依次输入下面指令:
$ export COPPELIASIM_ROOT_DIR=~/path/to/coppeliaSim/folder $ ulimit -s unlimited #otherwise compilation might freeze/crash $ colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release -DLIBPLUGIN_DIR=$COPPELIASIM_ROOT_DIR/programming/libPlugin
OK,如上是重新编译的全部过程。
启动CoppeliaSim的指令如下:
$ ./coppeliaSim.sh
会发现如下接口启动,说明ROS 2接口一切正常。
无需担心warning……
开启一个案例玩耍起来(ros2InterfaceTopicPublisherAndSubscriber.ttt):
使用终端查看一下消息:
添加一个仿真时间:
选择sphere,添加子代码如下:
function subscriber_callback(msg)
-- This is the subscriber callback function
sim.addStatusbarMessage('subscriber receiver following Float32: '..msg.data)
end
function getTransformStamped(objHandle,name,relTo,relToName)
-- This function retrieves the stamped transform for a specific object
t=sim.getSystemTime()
p=sim.getObjectPosition(objHandle,relTo)
o=sim.getObjectQuaternion(objHandle,relTo)
return {
header={
stamp=t,
frame_id=relToName
},
child_frame_id=name,
transform={
translation={x=p[1],y=p[2],z=p[3]},
rotation={x=o[1],y=o[2],z=o[3],w=o[4]}
}
}
end
function sysCall_init()
-- The child script initialization
objectHandle=sim.getObjectAssociatedWithScript(sim.handle_self)
objectName=sim.getObjectName(objectHandle)
ros2InterfacePresent=simROS2
-- Prepare the float32 publisher and subscriber (we subscribe to the topic we advertise):
if ros2InterfacePresent then
publisher=simROS2.advertise('/simulationTime','std_msgs/Float32')
subscriber=simROS2.subscribe('/simulationTime','std_msgs/Float32','subscriber_callback')
end
end
function sysCall_actuation()
-- Send an updated simulation time message, and send the transform of the object attached to this script:
if ros2InterfacePresent then
simROS2.publish(publisher,{data=sim.getSimulationTime()})
simROS2.sendTransform(getTransformStamped(objectHandle,objectName,-1,'world'))
-- To send several transforms at once, use simROS2.sendTransforms instead
end
end
function sysCall_cleanup()
-- Following not really needed in a simulation script (i.e. automatically shut down at simulation end):
if rosInterfacePresent then
simROS.shutdownPublisher(publisher)
simROS.shutdownSubscriber(subscriber)
end
end
这时,可以使用:
$ ros2 topic echo /simulationTime
查看开启仿真的时间。
换一个案例(controlledViaRos2):
更多介绍,查看官方文档。使用版本为CoppeliaSim_Edu_V4_0_0_Ubuntu18_04。
附录(英文文档原文):
This tutorial will try to explain in a simple way how you can manage to have CoppeliaSim ROS 2 enabled, based on ROS 2 Dashing.
First of all you should make sure that you have gone through the official ROS tutorials, at least the beginner section. Then, we assume that you have the latest Ubuntu running, that ROS is installed, and that the workspace folders are set. Here also refer to the official documentation regarding the ROS 2 installation.
The general ROS functionality in CoppeliaSim is supported via the ROS Interface (libsimExtROS2Interface.so). The Linux distribution should include that file already compiled in CoppeliaSim/compiledROSPlugins, but it first needs to be copied to CoppeliaSim/, otherwise it won't be loaded. You might however experience plugin load problems, depending on your system specificities: make sure to always inspect the terminal window of CoppeliaSim for details about plugin load operations. Plugins are loaded when CoppeliaSim is launched. Also make sure to source the ROS environment prior to running CoppeliaSim.
If the plugin cannot be loaded, then you should recompile it by yourself. It is open source and can be modified as much as needed in order to support a specific feature or to extend its functionality. If specific messages/services/etc. need to be supported, make sure to edit files located in simExtROSInterface/meta/, prior to recompilation. There are 2 packages:
Above packages should be copied to your ros2_ws/src folder.
In order to build the packages, navigate to the ros2_ws folder and type:
$ export COPPELIASIM_ROOT_DIR=~/path/to/coppeliaSim/folder $ ulimit -s unlimited #otherwise compilation might freeze/crash $ colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release -DLIBPLUGIN_DIR=$COPPELIASIM_ROOT_DIR/programming/libPlugin
That's it! The packages should have been generated and compiled to an executable or library. Copy and paste the created files to the CoppeliaSim installation folder. The plugins are now ready to be used.
Now open a terminal, move to the CoppeliaSim installation folder and start CoppeliaSim. This is what you should have (or similar):
$ ./coppeliaSim.sh ... Plugin 'ROS2Interface': loading... Plugin 'ROS2Interface': load succeeded. ...
Upon succesful ROS2 Interface load, checking the available nodes gives this:
$ ros2 node list /sim_ros2_interface
In an empty CoppeliaSim scene, select an object, then attach a non-threaded child script to it with [Menu bar --> Add --> Associated child script --> non threaded]. Open the script editor for that script and replace the content with following:
function subscriber_callback(msg) -- This is the subscriber callback function sim.addStatusbarMessage('subscriber receiver following Float32: '..msg.data) end function getTransformStamped(objHandle,name,relTo,relToName) -- This function retrieves the stamped transform for a specific object t=sim.getSystemTime() p=sim.getObjectPosition(objHandle,relTo) o=sim.getObjectQuaternion(objHandle,relTo) return { header={ stamp=t, frame_id=relToName }, child_frame_id=name, transform={ translation={x=p[1],y=p[2],z=p[3]}, rotation={x=o[1],y=o[2],z=o[3],w=o[4]} } } end function sysCall_init() -- The child script initialization objectHandle=sim.getObjectAssociatedWithScript(sim.handle_self) objectName=sim.getObjectName(objectHandle) ros2InterfacePresent=simROS2 -- Prepare the float32 publisher and subscriber (we subscribe to the topic we advertise): if ros2InterfacePresent then publisher=simROS2.advertise('/simulationTime','std_msgs/Float32') subscriber=simROS2.subscribe('/simulationTime','std_msgs/Float32','subscriber_callback') end end function sysCall_actuation() -- Send an updated simulation time message, and send the transform of the object attached to this script: if ros2InterfacePresent then simROS2.publish(publisher,{data=sim.getSimulationTime()}) simROS2.sendTransform(getTransformStamped(objectHandle,objectName,-1,'world')) -- To send several transforms at once, use simROS2.sendTransforms instead end end function sysCall_cleanup() -- Following not really needed in a simulation script (i.e. automatically shut down at simulation end): if rosInterfacePresent then simROS.shutdownPublisher(publisher) simROS.shutdownSubscriber(subscriber) end end
Above script will publish the simulation time, and subscribe to it at the same time. It will also publish the transform of the object the script is attached to. You should be able to see the simulation time topic with:
$ ros2 topic list
To see the message content, you can type:
$ ros2 topic echo /simulationTime
Now load the demo scene ros2InterfaceTopicPublisherAndSubscriber.ttt, and run the simulation. The code in the child script attached to Vision_sensor will enable a publisher to stream the vision sensor's image, and also enable a subscriber to listen to that same stream. The subscriber applies the read data to the passive vision sensor, that is only used as a data container. So CoppeliaSim is streaming data, while listening to the same data! This is what is happening:
[Image publisher and image subscriber demo]
Try experimenting a little bit with the code. You can also visualize the image that CoppeliaSim streams with following command:
$ ros2 run image_view image_view image:=/visionSensorData
Had you been streaming simpler data, then you could also have visualized it with:
$ ros2 topic echo /visionSensorData
Now stop the simulation and load the demo scene controlTypeExamples/controlledViaRos2.ttt, and run the simulation. The robot is simplistic, and also behaving in a simplistic way for simplification purposes. It is controlled via the ROS2 Interface:
[External client application controlling the robot via ROS]
The child script attached to the robot, and running in a non-threaded fashion, is in charge of following:
While simulation is running, copy and paste a few times the robot. Notice that every copy is directly operational and independent. This is one of the many strengths of CoppeliaSim.
Now stop the simulation and open a new scene, then drag following model into it: Models/tools/ros2Interface helper tool.ttm. This model is constituted by a single customization script that offers following topic publishers and subscribers:
Have a look at the content of the customization script, that can be fully customized for various purposes. Try generating topic messages from the command line, for instance:
$ ros2 topic pub /startSimulation std_msgs/Bool '{data: true}' --once $ ros2 topic pub /pauseSimulation std_msgs/Bool '{data: true}' --once $ ros2 topic pub /stopSimulation std_msgs/Bool '{data: true}' --once $ ros2 topic pub /enableSyncMode std_msgs/Bool '{data: true}' --once $ ros2 topic pub /startSimulation std_msgs/Bool '{data: true}' --once $ ros2 topic pub /triggerNextStep std_msgs/Bool '{data: true}' --once $ ros2 topic pub /triggerNextStep std_msgs/Bool '{data: true}' --once $ ros2 topic pub /triggerNextStep std_msgs/Bool '{data: true}' --once $ ros2 topic pub /stopSimulation std_msgs/Bool '{data: true}' --once
In order to display the current simulation time, you could type:
$ ros2 topic echo /simulationTime
Finally, make sure to have a look at the remote API functionality and the BlueZero framework in CoppeliaSim: similarly to ROS, it allows for remote function execution, fast data streaming back and forth, is quite simple to use, leightweight and cross-platform. The remote API functionality is available for 7 different languages. Both, the remote APi and the BlueZero framework can be interesting alternatives to ROS in some cases.
~Fin~