在完成04-入门:https://zhangrelay.blog.csdn.net/article/details/112739671。

如果将webots丰富的机器人库和ros2结合呢???

官网教程非常简洁:

  • https://github.com/cyberbotics/webots_ros2/wiki/Tutorial-Create-Webots-Robot
  • https://github.com/cyberbotics/webots_ros2/wiki/Tutorial-Write-ROS2-Driver

掌握如上两篇即可,都是去年的旧文档了。

下面简单说明一下:

创建Webots-ros2机器人

如果需要从零搭建机器人模型和ROS2驱动参考官网“教程6”,此处不涉及。

  1. 将机器人的controller字段值更改为<extern>
  2. 保存此环境。
  3. 执行 ros2 launch webots_ros2_core robot_launch.py world:=/path/to/your/world.wbt

这里以irobot为例:

选择Robot:

找一找controller:

然后保存环境:

使用如下命令:

ros2 launch webots_ros2_core robot_launch.py world:=create_ros.wbt

注意有警告,稍后具体说明。

这就完成了一个机器人ros2配置,当然功能有各种bug。

需要参考第二个链接,有关如何改进ROS2接口的更多详细信息,“编写ROS2驱动程序”!

前情回顾:

简单解释一下警告!

[WARNING] [launch_ros.actions.node]: Parameter file path is not a file: nul

个人理解未必准确!

如上方式只是最简单的ros2接口,并非全功能的,即便是编写ros2驱动程序中教程介绍的,也只是基本功能。

此处以笔记04-epuck为例。

其ros2驱动如下:

"""ROS2 e-puck driver."""from math import pi
import rclpy
from rclpy.time import Time
from tf2_ros import StaticTransformBroadcaster
from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import TransformStamped
from webots_ros2_core.math.interpolation import interpolate_lookup_table
from webots_ros2_core.webots_differential_drive_node import WebotsDifferentialDriveNodeOUT_OF_RANGE = 0.0
INFRARED_MAX_RANGE = 0.04
INFRARED_MIN_RANGE = 0.009
TOF_MAX_RANGE = 1.0
DEFAULT_WHEEL_RADIUS = 0.02
DEFAULT_WHEEL_DISTANCE = 0.05685
NB_INFRARED_SENSORS = 8
SENSOR_DIST_FROM_CENTER = 0.035DISTANCE_SENSOR_ANGLE = [-15 * pi / 180,   # ps0-45 * pi / 180,   # ps1-90 * pi / 180,   # ps2-150 * pi / 180,  # ps3150 * pi / 180,   # ps490 * pi / 180,    # ps545 * pi / 180,    # ps615 * pi / 180,    # ps7
]DEVICE_CONFIG = {'camera': {'topic_name': ''},'robot': {'publish_base_footprint': True},'ps0': {'always_publish': True},'ps1': {'always_publish': True},'ps2': {'always_publish': True},'ps3': {'always_publish': True},'ps4': {'always_publish': True},'ps5': {'always_publish': True},'ps6': {'always_publish': True},'ps7': {'always_publish': True},'tof': {'always_publish': True}
}class EPuckDriver(WebotsDifferentialDriveNode):def __init__(self, args):super().__init__('epuck_driver',args,wheel_distance=DEFAULT_WHEEL_DISTANCE,wheel_radius=DEFAULT_WHEEL_RADIUS)self.start_device_manager(DEVICE_CONFIG)# Intialize distance sensors for LaserScan topicself.distance_sensors = {}for i in range(NB_INFRARED_SENSORS):sensor = self.robot.getDistanceSensor('ps{}'.format(i))sensor.enable(self.timestep)self.distance_sensors['ps{}'.format(i)] = sensorself.laser_publisher = self.create_publisher(LaserScan, '/scan', 1)self.tof_sensor = self.robot.getDistanceSensor('tof')if self.tof_sensor:self.tof_sensor.enable(self.timestep)else:self.get_logger().info('ToF sensor is not present for this e-puck version')laser_transform = TransformStamped()laser_transform.header.stamp = Time(seconds=self.robot.getTime()).to_msg()laser_transform.header.frame_id = 'base_link'laser_transform.child_frame_id = 'laser_scanner'laser_transform.transform.rotation.x = 0.0laser_transform.transform.rotation.y = 0.0laser_transform.transform.rotation.z = 0.0laser_transform.transform.rotation.w = 1.0laser_transform.transform.translation.x = 0.0laser_transform.transform.translation.y = 0.0laser_transform.transform.translation.z = 0.033self.static_broadcaster = StaticTransformBroadcaster(self)self.static_broadcaster.sendTransform(laser_transform)# Main loopself.create_timer(self.timestep / 1000, self.__publish_laserscan_data)def __publish_laserscan_data(self):stamp = Time(seconds=self.robot.getTime()).to_msg()dists = [OUT_OF_RANGE] * NB_INFRARED_SENSORSdist_tof = OUT_OF_RANGE# Calculate distancesfor i, key in enumerate(self.distance_sensors):dists[i] = interpolate_lookup_table(self.distance_sensors[key].getValue(), self.distance_sensors[key].getLookupTable())# Publish range: ToFif self.tof_sensor:dist_tof = interpolate_lookup_table(self.tof_sensor.getValue(), self.tof_sensor.getLookupTable())# Max range of ToF sensor is 2m so we put it as maximum laser range.# Therefore, for all invalid ranges we put 0 so it get deleted by rvizlaser_dists = [OUT_OF_RANGE if dist > INFRARED_MAX_RANGE else dist for dist in dists]msg = LaserScan()msg.header.frame_id = 'laser_scanner'msg.header.stamp = stampmsg.angle_min = - 150 * pi / 180msg.angle_max = 150 * pi / 180msg.angle_increment = 15 * pi / 180msg.range_min = SENSOR_DIST_FROM_CENTER + INFRARED_MIN_RANGEmsg.range_max = SENSOR_DIST_FROM_CENTER + TOF_MAX_RANGEmsg.ranges = [laser_dists[3] + SENSOR_DIST_FROM_CENTER,   # -150OUT_OF_RANGE,                               # -135OUT_OF_RANGE,                               # -120OUT_OF_RANGE,                               # -105laser_dists[2] + SENSOR_DIST_FROM_CENTER,   # -90OUT_OF_RANGE,                               # -75OUT_OF_RANGE,                               # -60laser_dists[1] + SENSOR_DIST_FROM_CENTER,   # -45OUT_OF_RANGE,                               # -30laser_dists[0] + SENSOR_DIST_FROM_CENTER,   # -15dist_tof + SENSOR_DIST_FROM_CENTER,         # 0laser_dists[7] + SENSOR_DIST_FROM_CENTER,   # 15OUT_OF_RANGE,                               # 30laser_dists[6] + SENSOR_DIST_FROM_CENTER,   # 45OUT_OF_RANGE,                               # 60OUT_OF_RANGE,                               # 75laser_dists[5] + SENSOR_DIST_FROM_CENTER,   # 90OUT_OF_RANGE,                               # 105OUT_OF_RANGE,                               # 120OUT_OF_RANGE,                               # 135laser_dists[4] + SENSOR_DIST_FROM_CENTER,   # 150]self.laser_publisher.publish(msg)def main(args=None):rclpy.init(args=args)epuck_controller = EPuckDriver(args=args)rclpy.spin(epuck_controller)rclpy.shutdown()if __name__ == '__main__':main()

看完这段代码,就能理解04-入门中,laserscan数据的特点:

        msg = LaserScan()msg.header.frame_id = 'laser_scanner'msg.header.stamp = stampmsg.angle_min = - 150 * pi / 180msg.angle_max = 150 * pi / 180msg.angle_increment = 15 * pi / 180msg.range_min = SENSOR_DIST_FROM_CENTER + INFRARED_MIN_RANGEmsg.range_max = SENSOR_DIST_FROM_CENTER + TOF_MAX_RANGEmsg.ranges = [laser_dists[3] + SENSOR_DIST_FROM_CENTER,   # -150OUT_OF_RANGE,                               # -135OUT_OF_RANGE,                               # -120OUT_OF_RANGE,                               # -105laser_dists[2] + SENSOR_DIST_FROM_CENTER,   # -90OUT_OF_RANGE,                               # -75OUT_OF_RANGE,                               # -60laser_dists[1] + SENSOR_DIST_FROM_CENTER,   # -45OUT_OF_RANGE,                               # -30laser_dists[0] + SENSOR_DIST_FROM_CENTER,   # -15dist_tof + SENSOR_DIST_FROM_CENTER,         # 0laser_dists[7] + SENSOR_DIST_FROM_CENTER,   # 15OUT_OF_RANGE,                               # 30laser_dists[6] + SENSOR_DIST_FROM_CENTER,   # 45OUT_OF_RANGE,                               # 60OUT_OF_RANGE,                               # 75laser_dists[5] + SENSOR_DIST_FROM_CENTER,   # 90OUT_OF_RANGE,                               # 105OUT_OF_RANGE,                               # 120OUT_OF_RANGE,                               # 135laser_dists[4] + SENSOR_DIST_FROM_CENTER,   # 150]

echo数据中的0.0,其实没有对应传感器啊!!!也就是OUT_OF_RANGE。

重点说明:

由于官网教程比较简洁,如果想学好webots和ros2,请务必大量阅读源码,而不是满足于输入命令看到效果。

精彩继续:

通用启动器

webots-ros2提供了一个通用启动器,可以根据Webots的机器人说明自动创建ROS2服务和主题。只需在其中包含机器人即可提供Webots世界文件的路径,例如:

ros2 launch webots_ros2_core robot_launch.py \world:=$(ros2 pkg prefix webots_ros2_universal_robot --share)/worlds/universal_robot_rviz.wbt

自定义ROS2驱动程序和软件包

如果通用启动程序未涵盖Webots设备,或者希望以其他方式创建ROS接口,则可以从头开始构建ROS2驱动程序。

如果创建自定义包为my_webots_driver,编译成功后,可以新建驱动,如/my_webots_driver/my_webots_driver/driver.py

import rclpy
from webots_ros2_core.webots_node import WebotsNodeclass MyWebotsDriver(WebotsNode):def __init__(self, args):super().__init__('my_webots_driver', args=args)def main(args=None):rclpy.init(args=args)my_webots_driver = MyWebotsDriver(args=args)rclpy.spin(my_webots_driver)my_webots_driver.destroy()rclpy.shutdown()if __name__ == '__main__':main()

然后,在启动文件中加入如下说明:

import os
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch import LaunchDescription
from ament_index_python.packages import get_package_share_directorydef generate_launch_description():webots = IncludeLaunchDescription(PythonLaunchDescriptionSource(os.path.join(get_package_share_directory('webots_ros2_core'), 'launch', 'robot_launch.py')),launch_arguments=[('package', 'my_webots_driver'),('executable', 'driver'),('world', path_to_webots_world_file),])return LaunchDescription([webots])

确保这些添加到setup.py,并使用colcon build成功编译,才可以使用哦!

ros2 launch my_webots_driver robot_launch.py

使用webots的API实现距离传感器示例

当然官网还贴心的给了一个距离传感器示例,或者也可以直接参考epuck案例中对应部分:

class MyWebotsDriver(WebotsNode):def __init__(self, args):super().__init__('my_webots_driver', args=args)self.sensor = self.robot.getDistanceSensor('my_distance_sensor')self.sensor.enable(self.timestep)self.sensor_publisher = self.create_publisher(Range, '/my_distance_sensor', 1)self.create_timer(self.timestep * 1e-3, self.publish_sensor_data)def publish_sensor_data(self)msg = Range()msg.header.stamp = self.get_clock().now().to_msg()msg.header.frame_id = 'my_distance_sensor'msg.field_of_view = self.sensor.getAperture()msg.min_range = self.sensor.getMinValue()msg.max_range = self.sensor.getMaxValue()msg.range = self.sensor.getValue()msg.radiation_type = Range.INFRAREDself.sensor_publisher.publish(msg)

当然也可以加入如下扩展:

        self.start_device_manager({'my_distance_sensor': {'disable': True}})

更多内容推荐阅读文首的官网教程链接。


webots和ros2笔记05-新建相关推荐

  1. webots和ros2笔记08-分封

    如果阅读完webots_ros2源码,到此已经接近尾声了,为何?已经入门webots和ros2了. 是否需要继续研究就看需求了!推荐阅读下文: ROS2机器人操作系统零基础快速入门 https://z ...

  2. webots和ros2笔记03-解析

    在完成02-启程:https://zhangrelay.blog.csdn.net/article/details/112675018 那么会思考两个机械臂拿起易拉罐的过程是如何实现了. 简要分析一下 ...

  3. ESP32 单片机学习笔记 - 05 - AP/Smart Config

    ESP32 单片机学习笔记 - 05 - AP/Smart Config 终于把感觉必要的基础外设学完了,开始学esp32的主要特色功能--物联网~~?(大概) 一.WIFI热点 AP模式 编程指南: ...

  4. MyBatis-学习笔记05【05.使用Mybatis完成CRUD】

    Java后端 学习路线 笔记汇总表[黑马程序员] MyBatis-学习笔记01[01.Mybatis课程介绍及环境搭建][day01] MyBatis-学习笔记02[02.Mybatis入门案例] M ...

  5. JavaWeb黑马旅游网-学习笔记05【分类数据展示功能】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...

  6. Maven-学习笔记05【基础-使用骨架创建Maven的Java工程】

    Java后端 学习路线 笔记汇总表[黑马程序员] 黑马程序员(腾讯微云)Maven基础讲义.pdf Maven-学习笔记01[基础-Maven基本概念] Maven-学习笔记02[基础-Maven的安 ...

  7. Redis-学习笔记05【Jedis连接池】

    Java后端 学习路线 笔记汇总表[黑马程序员] Redis-学习笔记01[Redis环境搭建] Redis-学习笔记02[Redis命令操作] Redis-学习笔记03[Redis持久化] Redi ...

  8. JQuery-学习笔记05【高级——JQuery动画和遍历】

    Java后端 学习路线 笔记汇总表[黑马程序员] JQuery-学习笔记01[基础--JQuery基础]--[day01] JQuery-学习笔记02[基础--JQuery选择器] JQuery-学习 ...

  9. JavaWeb-综合案例(用户信息)-学习笔记05【分页查询功能】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb-综合案例(用户信息)-学习笔记01[列表查询] JavaWeb-综合案例(用户信息)-学习笔记02[登录功能] JavaWeb-综合案 ...

最新文章

  1. 模仿nginx修改进程名
  2. vmware 上部署 kvm虚拟机
  3. 使用WebRTC搭建前端视频聊天室——数据通道篇
  4. Polycarp's New Job
  5. [转]【无私分享:ASP.NET CORE 项目实战(第十四章)】图形验证码的实现
  6. word List 14
  7. 转:C++中const、volatile、mutable的用法
  8. Covariance and Contravariance in C#, Part One
  9. 报错,Error starting ApplicationContext. To display the conditions report re-run your application with
  10. java代码启动spring_从0开始学JAVA之《Spring框架-启动过程》
  11. org.springframework.dao.IncorrectResultSizeDataAccessException: query did not return a unique result
  12. 如何创建xsl文件 xml_EXCEL知识分享 I 连载如何快速创建XML文件
  13. CSS中min-height:100%问题
  14. Mac版 matlab 安装 GAOT工具箱
  15. android wps翻译功能吗,Android版WPS Office将支持金山词霸即时翻译
  16. Python案例笔记 | 用python制作二维码
  17. 男生和女生的十个瞬间 (温馨啊)【转载】
  18. 什么是垂直搜索? 推荐几个网站
  19. Android基础知识【项目实训-实现二级导航“今日活动”及读取数据库】【5】
  20. 高中信息技术c语言编程题,高中信息技术招聘C语言编程题精选.doc

热门文章

  1. GEA 3.1 重温C++以及实践
  2. 软件行业的发展要尊重软件工程的价值规律
  3. 以下是一些提供技术专利申请模板的中文网站,供您参考
  4. 簇大小与分区4k对齐的关系
  5. 二进制小数转换为十进制数和
  6. 详解SQL Server STUFF的用法
  7. char* strlen()学习
  8. 近期对战体会 [20170102]
  9. C语言程序设计笔记(浙大翁恺版) 第十周:字符串
  10. Mac装机必备之拯救歌荒,好用的五款Mac音乐播放器推荐