交大荣昶杯游记Day2
1 ROS2与视觉操作
1.1 前世今生
201几年,服务机器人,斯坦福大学本来想做一个家务活的机器人,其中最复杂的一项是叠衣服,“不重复造轮子”,开发一套系统,目的:比较复杂的系统分成各个模块;将模块作为通用性的模块,用到其他机器人上。
ROS:Robot Operating System 机器人操作系统。他只是Linux发行版,用于机器人上,但不是真正的操作系统。分布式构建,数据在各个节点上流通。开源系统,庞大的生态社区。
1.2 工作空间和功能包
工作空间(workspace),在 dev_ws
目录下。
下面有五个子目录,其中前四个较为重要:
src
:代码空间install
:安装空间build
:编译空间log
:日志空间config
:配置文件
调用ROS有两种方法:
- 调用单个指令(aka “节点”):
ros2 run
1
ros2 run teleop_twist_keyboard teleop_twist_keyboard
功能包:功能相似的节点的集合,放到一起去,如 teleop_twist_keyboard
。
例如,我们要制作的就是一个小车车道线识别的一个功能包。
- 运行多个节点,相当于批处理:
ros2 launch
,文件名需要为***.launch.py
。就不用你开很多终端会话运行很多个ros2 run
了。
1
ros2 launch originbot_bringup originbot.launch.py
1.3 ROS2命令行操作
- 查看节点信息:
ros2 node list
,多个节点之间实现消息的互通,来操控小车。 - 查看话题信息:
ros2 topic list
ros2 topic info <topic_name>
:查看话题的详细信息ros2 topic echo <topic_name>
:查看话题的消息
1.4 小车运动参数
一个物体,有六个自由度,其中三个方向的直线运动,再加上三个角度的旋转运动。
对小车而言,它是在平面上运动的,只剩下两个自由度:直线运动(沿x轴),转弯(绕z轴)。
所以想要控制小车,只需要给它两个值:一个是线性速度,另一个是角速度。
- 话题:
cmd_vel
- 消息:
Twist
(标准) from geometry_msgs.msg import Twist
- 速度:
twist.linear.x
- 转角:
twist.angular.z
- 使用:
1
2
3
4
cmd_vel_pub = create_publisher(Twist, 'cmd_vel', 10)
twist.linear.x = 0.1
twist.angular.z = 0.2
cmd_vel_pub.publish(twist)
在这个案例中,键盘是一个节点,小车控制又是一个节点,键盘按下,转成一个消息,通过话题传给小车控制节点,小车控制节点接收到消息,解析消息,控制小车。
1.5 观测数据流动
第一位同学:
1
ros2 launch originbot_bringup originbot.launch.py
第二位同学:
1
ros2 run teleop_twist_keyboard teleop_twist_keyboard
第三位同学:
1
2
3
4
ros2 node list
ros2 topic list
ros2 topic info /cmd_vel
ros2 topic echo /cmd_vel
ros2 topic echo /cmd_vel
这条指令会显示小车的运动参数。当没有运动时,将什么都不显示,否则:
1.6 直接控制小车运动参数
通过发布话题消息控制小车
1
ros2 topic pub --rate 1 /cmd_vel geometry_msgs/msg/Twist "linear: x: 2.0, y: 0.0, z: 0.0, angular: x: 0.0, y: 0.0, z: 1.8"
正如上面所说的,指定了 $x$ 和 $z$ 参数,就能控制运动了。
实验表明,$x$ 为正时向前走,$x$ 为负时往后走;
$z$ 为正时向左转,$z$为负时向右转。
/userdata/dev_ws/src/originbot/originbot_demo/originbot_demo/draw_circle.py
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"""
创建一个发布者节点
"""
class PublisherNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.pub = self.create_publisher(Twist, 'cmd_vel', 10) # 创建发布者对象(消息类型、话题名、队列长度)
self.timer = self.create_timer(0.5, self.timer_callback) # 创建一个定时器(单位为秒的周期,定时执行的回调函数)
def timer_callback(self): # 创建定时器周期执行的回调函数
twist = Twist() # 创建一个Twist类型的消息对象
twist.linear.x = 0.2 # 填充消息对象中的线速度
twist.angular.z = 0.8 # 填充消息对象中的角速度
self.pub.publish(twist) # 发布话题消息
self.get_logger().info('Publishing: "linear: %0.2f, angular: %0.2f"' % (twist.linear.x, twist.angular.z))
可以修改线速度和角速度。注意,The 'z' (or 'x') field must be of type 'float'
。修改完程序以后,在 dev_ws
目录下执行:
1
colcon build --packages-select originbot_demo --symlink-install
这里 --packages-select
指定只编译 demo
功能包,其他的不要构建,这样速度不会太慢。
--symlink-install
非常好用👍!在构建的时候加上这个参数,然后修改源代码后,重开进程,就能实时看到效果。不需要再编译一遍。
编译(约 $1 min$ )完之后,执行:
1
2
ros2 launch originbot_bringup originbot.launch.py
ros2 run originbot_demo draw_circle
1.7 工作空间和功能包
创建工作空间:mkdir testname_ws/src -p
testname_ws
就是工作空间的名称。
创建功能包:在 testname_ws/src
目录下,
1
ros2 pkg create --build-type ament_python learning_pkg_python
learning_pkg_python
就是功能包的名称。
Python源程序放置在 dev_ws/src/learning_pkg_python/learning_pkg_python
下。
- 修改
setup.py
:修改dev_ws/src/learning_pkg_python/setup.py
,在最后增加'node_helloworld=learning_pkg_python.node_helloworld:main',
,即:
运行程序的名称=功能包名称.源程序名称:main
第一个名称可以随便取,叫阿猫阿狗都行。功能包名称、源程序名称都是自己创建的时候指定的,千万不能随心所欲!
两个名称不要相同。后面不要 .py
。
然后可以在你的工作空间下编译:
在 testname_ws/
下执行: colcon build
编译成功后,source install/setup.bash
,使得 ros 能找到我们的包,否则会:
两个窗口都要 source
!
运行:ros2 run learning_pkg_python node_helloworld
1.8 Launch 文件的创建
进入这个目录:
新建文件夹 launch
。进入目录,再创建一个 simple.launch.py
。
simple.launch.py
的内容是(注意修改其中的自定义信息,功能包名称和可执行文件名称):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from launch import LaunchDescription # launch文件的描述类
from launch_ros.actions import Node # 节点启动的描述类
def generate_launch_description(): # 自动生成launch文件的函数
return LaunchDescription([ # 返回launch文件的描述信息
Node( # 配置一个节点的启动
package='learning_pkg_diyanqi', # 节点所在的功能包
executable='node_helloworld', # 节点的可执行文件
),
Node( # 配置一个节点的启动
package='learning_pkg_diyanqi', # 节点所在的功能包
executable='node_helloworld2', # 节点的可执行文件名
),
])