ROS安装教程和入门笔记
大四做完毕设闲着没事打算学习一下无人机仿真,然后就开始了解ROS,研究生阶段涉及仿真时或许可以用上。学习过程中主要记录了用到的教程以及遇到的一些坑,限于篇幅没有完全记录教程中的所有细节,所以这篇文章的使用方式应该是根据给出的教程参考链接打开原来的教程,按照原来的教程学习,在学习进行到某一步遇到问题时,可以看看这篇文章在这一步是否遇到了相同的问题,如果有则方便参考,没有只能自行探索解决办法了。希望这篇文章能给其他人入门提供一些帮助。
1. ROS中文入门教程
1.1安装ROS
安装时我参考的:一篇博客
安装和配置环境一般都比较麻烦,所谓从入门到放弃应该就是从这里开始的吧,只是环境配置就花费了一两天时间。在安装ROS的时候我用国内源提示找不到包,所以安装ROS的时候我换回Ubuntu官方源了(换回方法),但是在国内访问国外的网络特别慢而且容易出问题(懂得都懂),导致有些中途有些包没有下载下来而报错,根据终端提示用fixmissing参数再次尝试,一直尝试到所有包下载完毕就行。。。。
安装完毕后需要运行 sudo rosdep init
,可能因为网络问题失败,解决办法参考这个:解决sudo rosdep init
问题
解决完上面的init问题,再运行rosdep update
,同样可能因为网络问题失败,如果遇到问题参考这个:解决rosdep update
问题
1.2 ROS基本知识
学习时参考的:ROS官方中文入门教程
这个官方的教程解释的很清楚,所以学习这一部分时几乎没有遇到错误。看的时候忘记记笔记了,后面有机会复习的时候补充。
2. TF学习
兴致冲冲地以为可以开始对PR2机器人进行仿真了,但是发现教程要求需要首先了解物理仿真软件Gazebo教程,点开Gazebo教程,发现在Gazebo仿真软件里面创建模型需要通过URDF模型结构进行创建,然后点开URDF教程,又发现建立一个模型涉及很多相对坐标系,所以需要先了解TF包方便坐标变换。感觉自己被套娃了:),无奈先从TF包开始。
学习时参考的:TF教程
2.1 TF示例
教程首先给出了需要创建工作空间:
source /opt/ros/noetic/setup.bash
mkdir -p ~/tutorial_ws/src
cd ~/tutorial_ws
catkin_init_workspace src
catkin_make
source devel/setup.bash
然后给出了一个示例,运行示例roslaunch turtle_tf2 turtle_tf2_demo.launch
的时候遇到了process has died问题,看错误提示发现有两个原因,第一个原因是import yaml失败,所以需要pip install pyyaml
另一个原因是python版本问题,需要把下面文件夹的所有py文件第一行的#!/usr/bin/env python
替换为#!/usr/bin/env python3
2.2 写一个Static Broadcaster(Python)
用catking_create_pkg
创建一个名为learning_tf2的软件包,需要给出依赖。命令如下:
catkin_create_pkg learning_tf2 tf2 tf2_ros roscpp rospy turtlesim
进入目录:
roscd learning_tf2
创建节点文件夹:
mkdir nodes
在节点文件夹创建py文件:static_turtle_tf2_broadcaster.py
,直接复制教程中给出的代码
添加执行权限,但是好像这里py文件路径跟上面创建的py文件路径不一致,所以我手动把py文件放到这行代码对应的位置了,像这样:
然后再运行命令添加执行权限:
chmod +x ~/tutorial_ws/src/learning_tf2/src/nodes/static_turtle_tf2_broadcaster.py
回到工作空间文件夹:cd ~/tutorial_ws
,教程中直接给出了编译命令,但是下面这个CMakeLIst.txt还没设置好,我记得ROS中文官方教程里面是需要先设置后编译的,所以这里也需要设置一下,像下面这样把刚刚py文件的路径添加进去:
然后执行命令编译:
catkin_make
之后再根据教程的步骤,完成topic发布和查看。
. devel/setup.bash
roscore
rosrun learning_tf2 static_turtle_tf2_broadcaster.py mystaticturtle 0 0 1 0 0 0
rostopic echo /tf_static
2.3 写一个Broadcaster(python)
在刚才的nodes文件夹添加文件turtle_tf2_broadcaster.py
,复制原教程的代码。
添加权限
chmod +x nodes/turtle_tf2_broadcaster.py
创建start_demo.launch
文件,在下面这个文件夹,代码复制原教程的:
Build刚刚的代码文件,第一步先修改这个CMakeList.txt,166行是新加的,165行是上面2.2节那里加的。还有下面那一段取消注释,然后加上刚刚创建的launch文件:
然后cd到~/tf_tutorial_ws:catkin_make
开始编译。
运行:roslaunch learning_tf2 start_demo.launch
另一个终端运行:rosrun tf tf_echo /world /turtle1
查看结果
2.4 写一个Listener(Python)
根据原教程,创建文件nodes/turtle_tf2_listener.py
添加执行权限:
chmod +x nodes/turtle_tf2_listener.py
修改之前的start_demo.launch
,添加一个node:
<launch>
...
<node pkg="learning_tf2" type="turtle_tf2_listener.py"
name="listener" output="screen"/>
</launch>
重新编译:注意修改CMakeList.txt,标明新的py文件的位置。
运行并查看结果:
roslaunch learning_tf2 start_demo.launch
2.5 添加一个坐标系Frame(Python)
这一节创建了通过Broadcaster创建了一个固定的和时变的坐标系,方法与之前创建Broadcaster一样。
2.6 了解tf2和时间
while not rospy.is_shutdown():
try:
trans = tfBuffer.lookup_transform('turtle2',
'carrot1',
rospy.Time.now(),
rospy.Duration(1.0))
2.7 跨时间的tf2
try:
past = rospy.Time.now() - rospy.Duration(5.0)
trans = tfBuffer.lookup_transform(turtle_name, 'carrot1', past, rospy.Duration(1.0))
except (tf2_ros.LookupException, tf2_ros.ConnectivityException, tf2_ros.ExtrapolationException):
这是根据5秒前carrot1相对于5秒前turtle2的位置对现在进行控制,因为从现在的turtle2看起来是混乱的。
try:
past = rospy.Time.now() - rospy.Duration(5.0)
trans = tfBuffer.lookup_transform_full(
target_frame=turtle_name,
target_time=rospy.Time.now(),
source_frame='carrot1',
source_time=past,
fixed_frame='world',
timeout=rospy.Duration(1.0)
)
这是根据5秒前carrot1相对于现在turtle2的位置对现在进行控制,因此现在的turtle2追踪的是5秒前carrot1的位置。
2.8 四元数基础
注意tf2定义的四元数顺序是x/y/z/axes
, 所以不进行旋转的四元数表示为(0,0,0,1)
直接创建四元数消息:
from geometry_msgs.msg import Quaternion
# Create a list of floats, which is compatible with tf
# quaternion methods
quat_tf = [0, 1, 0, 0]
quat_msg = Quaternion(quat_tf[0], quat_tf[1], quat_tf[2], quat_tf[3])
通过欧拉矩阵创建消息:(旋转顺序RPY: Row, Pitch, Yaw)
from tf.transformations import quaternion_from_euler
if __name__ == '__main__':
# RPY to convert: 90deg, 0, -90deg
q = quaternion_from_euler(1.5707, 0, -1.5707)
print "The quaternion representation is %s %s %s %s." % (q[0], q[1], q[2], q[3])
坐标旋转直接用四元数乘法就行:
from tf.transformations import *
q_orig = quaternion_from_euler(0, 0, 0)
q_rot = quaternion_from_euler(pi, 0, 0)
q_new = quaternion_multiply(q_rot, q_orig)
print q_new
逆(转置):
感觉官网给出来的不太对:
q[3] = -q[3]
不应该是下面这样吗?:
q[:2] = -q[:2]
求相对旋转:
q1_inv[0] = prev_pose.pose.orientation.x
q1_inv[1] = prev_pose.pose.orientation.y
q1_inv[2] = prev_pose.pose.orientation.z
q1_inv[3] = -prev_pose.pose.orientation.w # Negate for inverse
q2[0] = current_pose.pose.orientation.x
q2[1] = current_pose.pose.orientation.y
q2[2] = current_pose.pose.orientation.z
q2[3] = current_pose.pose.orientation.w
qr = tf.transformations.quaternion_multiply(q2, q1_inv)
2.9 调试 tf2
没找到start_debug_demo.launch文件。。。
查看frame结构的命令:
rosrun tf2_tools view_frames
evince frames.pdf
检查时间戳:
rosrun tf2 tf2_monitor turtle2 turtle1
2.10 tf2使用传感器信息
2.11设置机器人用tf2
3. URDF模型学习
看完tf2部分后,进入到URDF建立机器人模型阶段,看看学完之后能不能创建一个简单的机器人模型。
3.1 用URDF从Scratch建立一个可视机器人模型
roscd urdf_tutorial
roslaunch urdf_tutorial display.launch model:=urdf/01-myfirst.urdf
注意fixed frame是网格的中心,是由urdf文件中base_link自己定义的。显示的圆柱的中心默认在网格中心。
再来创建一个多形状的物体(程序文件中已经自带了,所以直接显示):
roslaunch urdf_tutorial display.launch model:=urdf/02-multipleshapes.urdf
以及另一个改变joint位置和link位置的urdf文件,主要理解怎么定义文件中joint的位置和link的位置:
roslaunch urdf_tutorial display.launch model:=urdf/03-origins.urdf
在运行这个launch文件的时候会自动根据URDF文件创建TF坐标。
根据教程再分别看看怎么添加表面材料,以及完整的模型:
roslaunch urdf_tutorial display.launch model:=urdf/04-materials.urdf
roslaunch urdf_tutorial display.launch model:=urdf/05-visual.urdf
最后机器人的手指是用的dae文件:
<link name="left_gripper">
<visual>
<origin rpy="0.0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="package://urdf_tutorial/meshes/l_finger.dae"/>
</geometry>
</visual>
</link>
3.2 创建一个可移动机器人模型
joints类型除了fixed
, 还有continuous(连续转动副)
, revolute(转动副)
, prismatic(移动副或称棱柱副)
类型。其实还有其实还有planar
平面移动副和float
空间移动副,教程未具体解释。
头与身体的连续转动副示例:
<joint name="head_swivel" type="continuous">
<parent link="base_link"/>
<child link="head"/>
<axis xyz="0 0 1"/>
<origin xyz="0 0 0.3"/>
</joint>
注意转动副定义的旋转轴(0,0,1)
,显然头是绕Z轴旋转的。
夹持器的转动副示例:
<joint name="left_gripper_joint" type="revolute">
<axis xyz="0 0 1"/>
<limit effort="1000.0" lower="0.0" upper="0.548" velocity="0.5"/>
<origin rpy="0 0 0" xyz="0.2 0.01 0"/>
<parent link="gripper_pole"/>
<child link="left_gripper"/>
</joint>
注意转动副还要定义limit
: effort
:力气,可以施加的最大力(矩),lower
:最小角度, upper
:最大角度, velocity
:转动速度。
3.3 给URDF模型添加物理和碰撞属性
在一个link里面添加碰撞属性:
<collision>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</collision>
在一个link里面添加物理属性:
惯性(质量和转动惯量):
<inertial>
<mass value="10"/>
<inertia ixx="0.4" ixy="0.0" ixz="0.0" iyy="0.4" iyz="0.0" izz="0.2"/>
</inertial>
接触系数:
mu
: 摩擦系数
kp
:刚度系数
kd
:阻尼系数
副的动力学:
friction
:静摩擦力或摩擦力矩
damping
:动摩擦系数
3.4 使用Xacro简化URDF文件
3.4.1 Xacro
Xacro是XML的宏观语言(macro language)。
使用以下形式的命令可以将.xacro文件转化为urdf文件:
xacro --inorder model.xacro > model.urdf
在新版本中--inorder可以省略。
可以在.launch文件写这个命令,运行时自动生成urdf文件,例如:
<param name="robot_description"
command="xacro --inorder '$(find pr2_description)/robots/pr2.urdf.xacro'" />
一个Xacro文件必须在开头包含下面两行才能正确解释:
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="your thing's name">
之前的那个模型的base_link里面有些参数重复了两遍,改起来很麻烦:
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
<material name="blue"/>
</visual>
<collision>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</collision>
</link>
利用xacro属性名,可以这样写:
<xacro:property name="width" value="0.2" />
<xacro:property name="bodylen" value="0.6" />
<link name="base_link">
<visual>
<geometry>
<cylinder radius="${width}" length="${bodylen}"/>
</geometry>
<material name="blue"/>
</visual>
<collision>
<geometry>
<cylinder radius="${width}" length="${bodylen}"/>
</geometry>
</collision>
</link>
甚至属性名可以这样用(代替一部分):
<xacro:property name=”robotname” value=”marvin” />
<link name=”${robotname}s_leg” />
等价于:
<link name=”marvins_leg” />
甚至做数学操作:
<cylinder radius="${wheeldiam/2}" length="0.1"/>
<origin xyz="${reflect*(width+.02)} 0 0.25" />
3.4.2 Macro: xacro中最常用的包
定义一个简单的xacro:macro:
<xacro:macro name="default_origin">
<origin xyz="0 0 0" rpy="0 0 0"/>
</xacro:macro>
<xacro:default_origin />
最后会生成:
<origin rpy="0 0 0" xyz="0 0 0"/>
参数化的macro:
<xacro:macro name="default_inertial" params="mass">
<inertial>
<mass value="${mass}" />
<inertia ixx="1.0" ixy="0.0" ixz="0.0"
iyy="1.0" iyz="0.0"
izz="1.0" />
</inertial>
</xacro:macro>
<xacro:default_inertial mass="10"/>
最后会生成:
<inertial>
<mass value="10" />
<inertia ixx="1.0" ixy="0.0" ixz="0.0"
iyy="1.0" iyz="0.0"
izz="1.0" />
</inertial>
3.4.3 实际使用
如果需要查看由xacro文件产生的模型,同样用下面的命令:
roslaunch urdf_tutorial display.launch model:=urdf/08-macroed.urdf.xacro
如果模型中存在对称的模块或者对称的位置,那么运用macro将会方便很多,具体示例查看上面命令中的文件08-macroed.urdf.xacro
一些技巧:
用名字前缀来定义两个名字相似的物体
用数学计算joint的orgin,这样在改变物体尺寸的时候会省去很多麻烦
用镜像参数描述镜像针对的物体,将参数分别设置为-1和1
3.5 在Gazebo中使用URDF
打开教程的launch文件,尽量根据教程理解launch怎么写的:
roslaunch urdf_sim_tutorial gazebo.launch
为了让机器人可交互,需要指定插件Plugins和广播消息Transmissions.
为了连接Gazebo和ROS,我们在URDF</robot>
标签中指定了插件:
<gazebo>
<plugin name="gazebo_ros_control" filename="libgazebo_ros_control.so">
<robotNamespace>/</robotNamespace>
</plugin>
</gazebo>
可以在urdf/09-publishjoints.urdf.xacro
文件中查看
还需要引用控制器:
These are initially loaded into the ROS parameter space. We have a yaml file joints.yaml that specifies our first controller.
type: "joint_state_controller/JointStateController"
publish_rate: 50
在09-joints.launch
中给出了怎么加载这个yaml文件到r2d2_joint_state_controller
,但是现在还没完。。。
还需要广播消息:
对一每一个非固定joints,需要指定一个transmission,例如头的joint, 在URDF文件添加:
<transmission name="head_swivel_trans">
<type>transmission_interface/SimpleTransmission</type>
<actuator name="$head_swivel_motor">
<mechanicalReduction>1</mechanicalReduction>
</actuator>
<joint name="head_swivel">
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
</joint>
</transmission>
暂时把上面这个当作模板吧,现在我们运行下面的命令就可以看到joint的状态了:
roslaunch urdf_sim_tutorial 09-joints.launch model:=urdf/10-firsttransmission.urdf.xacro
rostopic echo /joint_states
接下来考虑怎么控制Joints
所以又需要添加一个控制器配置:
type: "position_controllers/JointPositionController"
joint: head_swivel
This specifies to use the a JointPositionController from the position_controllers package to control the head_swivel transmission.
Note that hardware interface in the URDF for this joint matches the controller type 注意URDF文件的接口与控制器类型要对应
.
在10-head.launch
已经添加进去了,现在我们直接运行看看:
roslaunch urdf_sim_tutorial 10-head.launch
现在Gazebo已经订阅了一个新话题,你可以通过发布消息进行控制:
rostopic pub /r2d2_head_controller/command std_msgs/Float64 "data: -0.707"
但是joint会立刻变到该位置,因为没有给joint加限制,所以添加limit:
<joint name="head_swivel" type="continuous">
<parent link="base_link"/>
<child link="head"/>
<axis xyz="0 0 1"/>
<origin xyz="0 0 ${bodylen/2}"/>
<limit effort="30" velocity="1.0"/>
</joint>
运行:
roslaunch urdf_sim_tutorial 10-head.launch model:=urdf/11-limittransmission.urdf.xacro
采用类似的方式对其他joint进行控制,但是像夹持器这种可能需要组合控制,所以需要一个不同的控制器JointGroupPositionController
:
type: "position_controllers/JointGroupPositionController"
joints:
- gripper_extension
- left_gripper_joint
- right_gripper_joint
运行:
roslaunch urdf_sim_tutorial 12-gripper.launch
所以发布消息的时候需要发布一个数组:
rostopic pub /r2d2_gripper_controller/command std_msgs/Float64MultiArray "layout:
dim:
- label: ''
size: 3
stride: 1
data_offset: 0
data: [0, 0.5, 0.5]"
为了让机器人可以移动,需要控制机器人的轮子,在机器人的macro文件中给每个轮子添加transmission,注意接口的名字叫VelocityJointInterface
:
<transmission name="${prefix}_${suffix}_wheel_trans">
<type>transmission_interface/SimpleTransmission</type>
<actuator name="${prefix}_${suffix}_wheel_motor">
<mechanicalReduction>1</mechanicalReduction>
</actuator>
<joint name="${prefix}_${suffix}_wheel_joint">
<hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
</joint>
</transmission>
因为轮子在地上走,那么需要对轮子的材料和接触系数进行定义:
<gazebo reference="${prefix}_${suffix}_wheel">
<mu1 value="200.0"/>
<mu2 value="100.0"/>
<kp value="10000000.0" />
<kd value="1.0" />
<material>Gazebo/Grey</material>
</gazebo>
然后给每个轮子添加控制器,下面这个yaml文件就有点长了,用的控制器叫DiffDriveController 差速驱动控制器
:
type: "diff_drive_controller/DiffDriveController"
publish_rate: 50
left_wheel: ['left_front_wheel_joint', 'left_back_wheel_joint']
right_wheel: ['right_front_wheel_joint', 'right_back_wheel_joint']
wheel_separation: 0.44
# Odometry covariances for the encoder output of the robot. These values should
# be tuned to your robot's sample odometry data, but these values are a good place
# to start
pose_covariance_diagonal: [0.001, 0.001, 0.001, 0.001, 0.001, 0.03]
twist_covariance_diagonal: [0.001, 0.001, 0.001, 0.001, 0.001, 0.03]
# Top level frame (link) of the robot description
base_frame_id: base_link
# Velocity and acceleration limits for the robot
linear:
x:
has_velocity_limits : true
max_velocity : 0.2 # m/s
has_acceleration_limits: true
max_acceleration : 0.6 # m/s^2
angular:
z:
has_velocity_limits : true
max_velocity : 2.0 # rad/s
has_acceleration_limits: true
max_acceleration : 6.0 # rad/s^2
运行:
roslaunch urdf_sim_tutorial 13-diffdrive.launch
launch文件里面添加了一个机器人控制面板可以控制该机器人,cool!
URDF还有一些教程,后面可能需要深入学习, 但现在先开始Gazebo教程。
4 Gazebo学习
打开Gazebo教程,发现有一个提醒,提示现在这个教程已经不适用了:
但是还是按照过时的教程学了一部分,现在新的Gazebo官网里面有个Library名字好像叫Ignition,但是按照之前的教程一路下来配置的环境好像是安装的经典的Gazebo(Gazebo11)经典Gazebo官网。
首先有必要了解一下Gazebo和ROS是怎么连接的,这是一篇知乎文章的解释:
Gazebo与ROS 版本的集成是通过一组叫做gazebo_ros_pkgs
的包完成的,gazebo_ros_pkgs
不是一个包,而是一堆包:
gazebo_dev
:开发Gazebo插件可以用的APIgazebo_msgs
:定义的ROS2和Gazebo之间的接口(Topic/Service/Action)gazebo_ros
:提供方便的 C++ 类和函数,可供其他插件使用,例如转换和测试实用程序。它还提供了一些通常有用的插件。gazeboros::Nodegazebo_plugins
:一系列 Gazebo 插件,将传感器和其他功能暴露给 ROS2 例如:
原来是通过一系列包进行连接的,好像在Gazebo和ROS联合仿真时能够经常看见这些包,前面已经大概看到过了。现在暂时还是先看看过时的教程,后面会按照经典的Gazebo(Gazebo11)进行学习。
4.1 安装和开始Gazebo
这一部分在按照前面的教程安装ROS的时候已经装好了。
开始运行:
roslaunch gazebo_ros empty_world.launch
empty_world.launch
文件解释:
<launch>
<!-- start gazebo with an empty plane -->
<param name="/use_sim_time" value="true" />
<node name="gazebo" pkg="gazebo" type="gazebo" args="$(find gazebo_worlds)/worlds/empty.world" respawn="false" output="screen"/>
</launch>
4.2 创建和引用自定义URDF物体进行仿真
创建一个object.urdf
文件:
<robot name="simple_box">
<link name="my_box">
<inertial>
<origin xyz="2 0 0" />
<mass value="1.0" />
<inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="100.0" iyz="0.0" izz="1.0" />
</inertial>
<visual>
<origin xyz="2 0 1"/>
<geometry>
<box size="1 1 2" />
</geometry>
</visual>
<collision>
<origin xyz="2 0 1"/>
<geometry>
<box size="1 1 2" />
</geometry>
</collision>
</link>
<gazebo reference="my_box">
<material>Gazebo/Blue</material>
</gazebo>
</robot>
在bash中引入模型:
放置在z=1位置,并且命名为my_object
rosrun gazebo spawn_model -file `pwd`/object.urdf -urdf -z 1 -model my_object
用launch文件引入模型的例子(好像找不到这个文件,可能需要自己在相应的文件夹创建,主要学习一下代码就行):
roslaunch gazebo_worlds table.launch
table.launch
:
<launch>
<!-- send table urdf to param server -->
<param name="table_description" command="$(find xacro)/xacro.py $(find gazebo_worlds)/objects/table.urdf.xacro" />
<!-- push table_description to factory and spawn robot in gazebo -->
<node name="spawn_table" pkg="gazebo" type="spawn_model" args="-urdf -param table_description -z 0.01 -model table_model" respawn="false" output="screen" />
</launch>
这个文件首先把xacro文件加载到ROS参数服务器,然后利用spawn_model从参数服务器得到模型的XML字符转然后传递给Gazebo来在仿真世界中构建这个物体。
4.3 Gazebo ROS API for C-Turtle
一些术语:Gazebo里面的Body就和URDF里面的Link差不多,表示一个刚体。
4.3.1 添加和删除模型
使用gazebo/spawn_model
和gazebo/delete_model
:
添加模型示例:
rosrun gazebo spawn_model -file `rospack find gazebo_worlds`/objects/desk1.model -gazebo -model desk1 -x 0
使用帮助:
$ rosrun gazebo spawn_model
Commands:
-[urdf|gazebo] - specify incoming xml is urdf or gazebo format
-[file|param] [<file_name>|<param_name>] - source of the model xml
-model <model_name> - name of the model to be spawned.
-reference_frame <entity_name> - optinal: name of the model/body where initial pose is defined.
If left empty or specified as "world", gazebo world frame is used.
-namespace <ros_namespace> - optional: all subsequent ROS interface plugins will be inside of this namespace.
-x <x in meters> - optional: initial pose, use 0 if left out
-y <y in meters> - optional: initial pose, use 0 if left out
-z <z in meters> - optional: initial pose, use 0 if left out
-R <roll in radians> - optional: initial pose, use 0 if left out
-P <pitch in radians> - optional: initial pose, use 0 if left out
-Y <yaw in radians> - optional: initial pose, use 0 if left out
在命令行中也可以通过rosparam导入urdf模型:
rosparam load `rospack find gazebo_worlds`/objects/coffee_cup.model coffee_cup_description
rosrun gazebo spawn_model -param coffee_cup_description -gazebo -model coffee_cup -x 0 -z 2
删除模型示例:
rosservice call gazebo/delete_model '{model_name: coffee_cup}'
4.3.2 通过服务设置和获取模型位置和姿态
运行仿真环境:
rosrun gazebo spawn_model -file `rospack find gazebo_worlds`/objects/000.580.67.model -gazebo -model cup -z 2
但是好像现在已经没有gazebo_worlds
这个包了,参考网址,但是只需要学习后面怎么设置和获取模型位置和姿态就行。
通过/gazebo/set_model_state
设置模型位置和姿态:
rosservice call /gazebo/set_model_state '{model_state: { model_name: cup, pose: { position: { x: 0, y: 0 ,z: 1 }, orientation: {x: 0, y: 0.491983115673, z: 0, w: 0.870604813099 } }, twist: { linear: {x: 0.0 , y: 0 ,z: 0 } , angular: { x: 0.0 , y: 0 , z: 0.0 } } , reference_frame: world } }'
通过/gazebo/get_model_state
获取模型位置和姿态:
rosservice call gazebo/get_model_state '{model_name: cup}'
4.3.2 获取仿真的世界和模型的属性
rosservice call gazebo/get_world_properties
rosservice call gazebo/get_model_properties '{model_name: desk1}'
4.3.3 使用话题获取模型和Link的状态
rostopic echo -n 1 /gazebo/model_states
rostopic echo -n 1 /gazebo/link_states
模型的状态是指模型中典型Link的状态,一般是root link。
4.3.4 对Link(物体)添加和删除Wrench(扳手?)
rosservice call gazebo/apply_body_wrench '{body_name: "cup::000_580_67_body" , wrench: { torque: { x: 0.01, y: 0 , z: 0 } }, start_time: 10000000000, duration: 1000000000 }'
rosservice call gazebo/clear_body_wrenches '{body_name: "cup::000_580_67_body"}'
4.3.4 对Joints施加和删除Efforts(力气)
rosservice call gazebo/apply_joint_effort '{joint_name: link_joint, effort: 0.01, start_time: 10000000000, duration: 1000000000}'
rosservice call gazebo/clear_joint_forces '{joint_name: link_joint}'
4.3.5 暂停和启用物理引擎
rosservice call gazebo/pause_physics
rosservice call gazebo/unpause_physics
4.3.6 用话题设置模型位置和姿态
rostopic pub -r 10 gazebo/set_model_state gazebo/ModelState '{model_name: desk1, pose: { position: { x: 0, y: 0, z: 2 }, orientation: {x: 0, y: 0.491983115673, z: 0, w: 0.870604813099 } }, twist: { linear: { x: 0, y: 0, z: 0 }, angular: { x: 0, y: 0, z: 0} }, reference_frame: world }'
4.4 经典Gazebo教程
由于前面提到的教程过时了,前面的一部分或许有一定参考意义,但是最好还是按照经典的Gazebo教程进行学习!
Gazebo涉及的东西就有点多了,目前的想法是后面遇到了相关的内容再进一步了解,所以先进入PR2机器人教程
5 PR2机器人学习
5.1 创建场景
首先安装PR2包:
sudo apt install ros-noetic-pr2-*
在这一步还是遇到了网络问题。。。先后尝试了换成手机热点,尝试了手动下载,下载方式从Debian网站下载,好像没能解决,然后多试了几遍--fix-missing,然后又能连接上了。。。玄学。
roslaunch gazebo_ros empty_world.launch
roslaunch pr2_gazebo pr2.launch
运行完之后又报ImportError: No module named yaml, ImportError: No module named rospkg模块错误,这两个模块我已经装过了,但是是python3环境的,而PR2项目的代码是python2的,前面已经遇到过这个问题,需要根据错误找到那些py文件,把文件第一行的#!/usr/bin/env python
替换为#!/usr/bin/env python3
。
弄完之后还有一个错误:[ERROR] [1652446439.190980226]: Skipped loading plugin with error: XML Document '/opt/ros/noetic/share/prosilica_camera/plugins/nodelet_plugins.xml' has no Root Element. This likely means the XML is malformed or missing..
打开报错的那个文件夹发现原来这个nodelet_plugins.xml
的路径放错了,按照报错的那个位置新建一个plugins文件夹,把这个文件放进去就好了:
好,现在来看一下pr2.launch:
<launch>
<!-- Startup PR2 without any mechanism controllers -->
<include file="$(find pr2_gazebo)/pr2_no_controllers.launch" />
<!-- Load and Start Default Controllers -->
<include file="$(find pr2_controller_configuration_gazebo)/pr2_default_controllers.launch" />
</launch>
好像就是加载了一个无控制器的pr2,然后又加载了一个默认控制器的pr2
5.2 ROS和Gazebo交互:
先看看PR2的Joints状态:
rostopic echo joint_states | less
控制机器人:
rosmake pr2_teleop
roslaunch pr2_teleop teleop_keyboard.launch
可以用键盘控制!
Reading from keyboard
---------------------------
Use 'WASD' to translate
Use 'QE' to yaw
Press 'Shift' to run
5.3 PR2 仿真器工厂
主要探索PR2的接口
为了触发系统使用pr2_machine/sim.machine,将ROS_MASTER_URI导出到localhost,并作为sim。(不懂。。。)
export ROS_MASTER_URI=http://localhost:11311
export ROBOT=sim
然后运行环境:
roslaunch pr2_gazebo pr2_empty_world.launch
可以看看此时ROS节点,话题和服务,参数都有啥:试了一下发现每一项都有很多。
可以用rqt_pr2_dashboard
可视化机器人的状态:
rosrun rqt_pr2_dashboard rqt_pr2_dashboard
但是报错好像少了plugin.xml插件,这回我没有找到,不知道怎么解决了。。。跳过这个吧
可视化机器人:
rosrun rviz rviz
根据教程的操作可以进行一些可视化操作。
调用倾斜激光轨迹:
rosservice call laser_tilt_controller/set_periodic_cmd '{ command: { header: { stamp: 0 }, profile: "linear" , period: 3 , amplitude: 1 , offset: 0 }}'
然后用rviz看一下话题tilt_scan,全是有一个激光,暂时还不清楚这个干嘛用的:
利用URDF和STL文件添加自定义的物体:
一般可以把Gazebo的模型放在models文件夹里面,教程里面创建了一个桌子,因此放到table文件夹下面:
tabla.stl文件
table.urdf代码(官网的代码过时了,稍微改了一下):
<robot name="table_model">
<link name="table_body">
<inertial>
<mass value="1.0" />
<!-- center of mass (com) is defined w.r.t. link local coordinate system -->
<origin xyz="0.25 -0.5 0" />
<inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0" />
</inertial>
<visual>
<!-- visual origin is defined w.r.t. link local coordinate system -->
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="/opt/ros/noetic/share/gazebo_plugins/Media/models/table/table.stl" scale="0.01 0.01 0.01"/>
</geometry>
</visual>
<collision>
<!-- collision origin is defined w.r.t. link local coordinate system -->
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="/opt/ros/noetic/share/gazebo_plugins/Media/models/table/table.stl" scale="0.01 0.01 0.01"/>
</geometry>
</collision>
</link>
<gazebo reference="table_body">
<material>Gazebo/Red</material>
</gazebo>
</robot>
运行Gazebo:
roslaunch gazebo_ros empty_world.launch
将URDF文件添加进ROS参数服务,取名为robot_description
,之前没做这一步模型就显示不了!
rosparam set -t '/opt/ros/noetic/share/gazebo_plugins/Media/models/table/table.urdf' robot_description
然后生成模型:
rosrun gazebo_ros spawn_model -urdf -model mycar -param robot_description
可以看到桌子添加进去了!但是好像方位有点问题。。。具体模型需要具体调一下位置。
从开始安装ROS一直学到这里感觉才了解ROS,TF,URDF,Gazebo,Rviz的概念,但是怎么从零开始实现一个机器人,可能需要认真研读类似PR2这种项目的代码。但是在重复上面的仿真过程就因为代码过时而遇到不少坑,所以找找其他有用的资源。
我是想学习无人机仿真的,还记得最开始安装ROS参考的博客吗? 这位博主基于苏黎世理工大学的
rotors
包进行无人机从建模到控制的仿真,感觉可以用来加深对ROS的理解,有时间也可以跟着学习一下。
由于不清楚rotors后续能做到什么程度,我现在知道的用得比较多的一个飞行器仿真平台应该是PX4,已经有很多实际产品,相关的资源比较多,并且PX4可以跟ROS结合,因此打算后面主要学习
PX4+ROS
。
入门教程就先写到这里吧,虽然连半只脚都没有踏入,但是后面开了rotors
、PX4+ROS
两个新坑,在接下来的博客继续记录,以及本文第一章ROS基础知识会在后面复习的时候补充。