ROS1的使用方法,以noetic为例
安装ROS1:noetic
小鱼一键安装
wget http://fishros.com/install -O fishros && . fishros
问题:联网问题,依然安装比较繁琐
ROS官网
仓库地址
sudo sh -c '. /etc/lsb-release && echo "deb http://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/ `lsb_release -cs` main" > /etc/apt/sources.list.d/ros-latest.list'
设置密钥(去ros官网获取更新)
sudo apt-key adv --keyserver 'hkp://pgp.mit.edu:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
开始安装
sudo apt update
sudo apt install ros-noetic-desktop-full
环境变量
source /opt/ros/noetic/setup.bash
开机自动设置环境变量
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
source ~/.bashrc
实验:
roscore
检查:
sudo apt-get install -y ros-noetic-navigation
sudo apt-get install -y ros-noetic-robot-localization
sudo apt-get install -y ros-noetic-robot-state-publisher
rosbag的使用方法
ROS2和ROS1数据集的转换
ros最强大的地方在于开源工具多。
转换工具需要有如下特点:
● 可以无缝实现bag和db3的互相转换
● 提供了db3文件的读写接口,非常方便。
● 转换速度很快。
● 不用依赖ros2、ros1
这个工具就是
rosbags
更新对ros2 mcap格式数据支持。注意rosbags版本要大于等于0.9.15
pip install --upgrade rosbags
关键是这个包还不用依赖ros2和ros1,在python3下可以直接使用
其文档在操作文档
代码托管在gitlab上,github地址
安装
pip install rosbags
将ros2的db3转为bag,rosbag2_2023_01_31-14_40_45是文件夹,必须包含metadata.yaml文件
rosbags-convert rosbag2_2023_01_31-14_40_45
rosbags-convert --dst /home/john/rosbag/dcl/Campus_Road_1.bag Campus_Road_1
即可得到rosbag2_2023_01_31-14_40_45.bag
将bag文件转为ros2的db3
rosbags-convert rosbag2_2023_01_31-14_40_45.bag
从db3中读取msg
from rosbags.rosbag2 import Reader
from rosbags.serde import deserialize_cdr
# create reader instance and open for reading
with Reader('/home/xxx/data/rosbag2_2023_01_04-17_24_41') as reader:
connections = [x for x in reader.connections if x.topic == '/Imu']
for connection, timestamp, rawdata in reader.messages(connections=connections):
msg = deserialize_cdr(rawdata, connection.msgtype)
print(msg.header.frame_id)
Linux查看pcd云图方法
如果直接输入命令:
pcl_viewer xxx.pcd
提示没有相关软件包信息。
安装pcl工具包
输入命令:
sudo apt install pcl-tools
等待安装完毕。
输入打开pcd文件命令:
pcl_viewer xxx.pcd
ros中的节点、话题、TF树,以lio-sam为例
rqt_graph
rqt_tf_tree
c++和yaml文件
利用ROS自带的XmlRpc::XmlRpcValue实现一维数组或者二位数组等类似json数据的读取
yaml文件
这套代码需要很多设置
在$(find test_cpp)/config/config.yaml
# yaml文件
scanners:
- {pub_topic: "scan_head", frame_id: "laser_scanner_link_head",server_ip: "192.168.167.100",server_port: 2111}
- {pub_topic: "scan_middle",frame_id: "laser_scanner_link_middle",server_ip: "192.168.167.101",server_port: 2111}
- {pub_topic: "scan_tail",frame_id: "laser_scanner_link_tail",server_ip: "192.168.167.102",server_port: 2111}
merge_area:
car: {left: 0, top: 0.5, right: 1.0, bottom: 1.25}
bus: {left: 0.125, top: 0.5, right: 0.875, bottom: 1.25}
person: {left: -0.25, top: 0.75, right: 1.25, bottom: 1.25}
num_list: [37.783, 1.414, 3.14, 0.618]
C++读取代码
#include <ros/ros.h>
#include <std_msgs/String.h>
#include <string>
#include <XmlRpcValue.h> // 确保包含 XmlRpc 库
using namespace std;
int main(int argc, char **argv) {
ros::init(argc, argv, "path_show");
ros::NodeHandle nh("~");
// 字典数组的解析
XmlRpc::XmlRpcValue params; // 定义 params 变量
if (nh.getParam("/scanners", params)) { // 检查是否成功获取参数
if (params.getType() == XmlRpc::XmlRpcValue::TypeArray) { // 确保 params 是一个数组
for (size_t i = 0; i < params.size(); ++i) {
const std::string& server_ip = static_cast<std::string>(params[i]["server_ip"]);
const std::string& frame_id = static_cast<std::string>(params[i]["frame_id"]);
const std::string& pub_topic = static_cast<std::string>(params[i]["pub_topic"]);
const int server_port = static_cast<int>(params[i]["server_port"]); // 去掉引用,这里直接使用值
printf("ip:%s, id:%s, topic:%s, port:%d \r\n", server_ip.c_str(), frame_id.c_str(), pub_topic.c_str(), server_port);
}
} else {
ROS_ERROR("Expected 'scanners' to be an array");
}
} else {
ROS_ERROR("Failed to get param 'scanners'");
}
// 数组写入向量
XmlRpc::XmlRpcValue nums; // 定义 nums 变量
if (nh.getParam("/num_list", nums)) { // 检查是否成功获取参数
if (nums.getType() == XmlRpc::XmlRpcValue::TypeArray) { // 确保 nums 是一个数组
std::vector<double> num_list;
for (size_t i = 0; i < nums.size(); ++i) {
XmlRpc::XmlRpcValue tmp_value = nums[i];
if (tmp_value.getType() == XmlRpc::XmlRpcValue::TypeDouble) {
double value = static_cast<double>(tmp_value); // 类型转换
num_list.push_back(value);
printf("get one num: %f\r\n", value);
} else {
ROS_WARN("Element %zu is not a double", i);
}
}
} else {
ROS_ERROR("Expected 'num_list' to be an array");
}
} else {
ROS_ERROR("Failed to get param 'num_list'");
}
// 字典读取,注意类型对应
XmlRpc::XmlRpcValue area; // 定义 area 变量
if (nh.getParam("/merge_area", area)) { // 检查是否成功获取参数
if (area.hasMember("car")) { // 检查是否包含 'car' 字段
XmlRpc::XmlRpcValue car = area["car"];
if (car.getType() == XmlRpc::XmlRpcValue::TypeStruct) { // 确保 car 是一个字典
printf("car_area = top: %f, left: %d, bottom: %f, right: %f\r\n",
static_cast<double>(car["top"]),
static_cast<int>(car["left"]),
static_cast<double>(car["bottom"]),
static_cast<double>(car["right"]));
} else {
ROS_ERROR("'car' is not a struct");
}
} else {
ROS_ERROR("No 'car' member found in 'merge_area'");
}
} else {
ROS_ERROR("Failed to get param 'merge_area'");
}
}
注意类型的转换,需要注意一一对应,如果不对应,就会报错,(如:int不能使用double强转)
报错信息:terminate called after throwing an instance of ‘XmlRpc::XmlRpcException’
用launch启动的方法
<launch>
<node pkg="test_cpp" type="test_cpp" name="test" output="screen" />
<rosparam file="$(find test_cpp)/config/config.yaml" command="load"/>
<!-- </node> -->
<!-- <node pkg="learning_topic" type="person_publisher" name="listener" output="screen" /> -->
</launch>
1 launch文件结构(启动需要CMakeList)
● 由XML语言写的,可实现多个节点的配置和启动。
● 不再需要打开多个终端用多个rosrun命令来启动不同的节点了
● 可自动启动ROS Master
例子:
<launch>
<node pkg="learning_topic" type="person_subscriber" name="talker" output="screen" />
<node pkg="learning_topic" type="person_publisher" name="listener" output="screen" />
</launch>
<launch>
<!-- Turtlesim Node-->
<node pkg="turtlesim" type="turtlesim_node" name="sim"/>
<node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>
<node pkg="learning_tf" type="turtle_tf_broadcaster" args="/turtle1" name="turtle1_tf_broadcaster" />
<node pkg="learning_tf" type="turtle_tf_broadcaster" args="/turtle2" name="turtle2_tf_broadcaster" />
<node pkg="learning_tf" type="turtle_tf_listener" name="listener" />
</launch>
<launch>
<!-- Turtlesim Node-->
<node pkg="turtlesim" type="turtlesim_node" name="sim"/>
<node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>
<node name="turtle1_tf_broadcaster" pkg="learning_tf" type="turtle_tf_broadcaster.py">
<param name="turtle" type="string" value="turtle1" />
</node>
<node name="turtle2_tf_broadcaster" pkg="learning_tf" type="turtle_tf_broadcaster.py">
<param name="turtle" type="string" value="turtle2" />
</node>
<node pkg="learning_tf" type="turtle_tf_listener.py" name="listener" />
</launch>
<launch>
<param name="/turtle_number" value="2"/>
<node pkg="turtlesim" type="turtlesim_node" name="turtlesim_node">
<param name="turtle_name1" value="Tom"/>
<param name="turtle_name2" value="Jerry"/>
<rosparam file="$(find learning_launch)/config/param.yaml" command="load"/>
</node>
<node pkg="turtlesim" type="turtle_teleop_key" name="turtle_teleop_key" output="screen"/>
</launch>
<launch>
<include file="$(find learning_launch)/launch/simple.launch" />
<node pkg="turtlesim" type="turtlesim_node" name="turtlesim_node">
<remap from="/turtle1/cmd_vel" to="/cmd_vel"/>
</node>
</launch>
CMakeList.txt
cmake_minimum_required(VERSION 3.0.2)
project(test_cpp)
## 找到catkin和任何其他需要的包
find_package(catkin REQUIRED COMPONENTS
roscpp
rosmsg
rospy
std_msgs
)
## 声明catkin包
catkin_package()
## 指定包含目录
include_directories(
include
${catkin_INCLUDE_DIRS}
)
## 将源文件编译为可执行文件
add_executable(test_cpp src/test_cpp.cpp)
## 连接目标所需的库
target_link_libraries(test_cpp
${catkin_LIBRARIES}
)
## 安装可执行文件
install(TARGETS test_cpp
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
python文件
创建工作空间
mkdir -p ~/catkin_data/src
进入src文件夹:
cd catkin_date/src/
初始化文件夹(在src文件夹下):
catkin_init_workspace
创建功能包
2、进入data功能包
cd catkin_data/src/data/
创建launch文件夹和scripts文件夹
mkdir launch scripts
在launch文件夹中创建demo_py.launch,内容如下:
touch demo_py.launch
<launch>
<param name="date" value="2021-03-18" />
<param name="vvvv" value="5" />
<node pkg="demo_py" name="demo_py" type="hello.py" output="screen">
</node>
</launch>
name区别于type和pkg
● pkg:指定了节点所属的 ROS 包(在这里是 cut_rosbag_py)。
● type:指定了要运行的可执行文件或脚本(在这里是 cut_rosbag.py)。
● name:指定了节点的唯一名称(在这里是 cut_rosbag)。
在scripts文件中创建hello.py,内容如下:
touch hello.py
#!/usr/bin/env python
# coding:utf-8
import rospy
def gsp(data):
# 回调函数
rospy.loginfo("Received GPS position: latitude=%f, longitude=%f", data.latitude, data.longitude)
if __name__ == '__main__':
# 创建节点
rospy.init_node("pyhello")
print("hello Irvingao! this is ur first ros python code! Good luck for u!")
# 一个脚本只有一个节点,anonymous意思是:week_3662,防止和week节点重名
# rospy.init_node('week', anonymous=True)
print(rospy.has_param('/date')) # 是否存在参数
date=rospy.get_param('date') # 获取参数
record=rospy.get_param('vvvv')
print(record) # 打印参数
print(date)
# rospy.Subscriber('/minibus/gpsposition', GpsPosition, gsp)
rospy.spin()
可执行权限修改
cd scripts
chmod a+x hello.py
默认编写的py文件是不具备执行权限的。因此我们需要手动讲文件权限修改为可执行。
通过roslaunch运行
cd catkin_date/
编译工作空间
catkin_make
source devel/setup.bash
roslaunch demo_py demo_py.launch
运行后打印在launch文件中设置好的参数
launch文件需要放在某个package目录下,最好放在最主要的package目录下,醒目
launch文件是可以跨包的,不要因为roslaunch test_pkg test.launch运行在test_pkg下而认为只能执行test_pkg下的节点,只要配置好对应的包名,节点,和执行文件即可
launch文件后缀必须是.launch
output 设置成screen是输出到屏幕,默认是输出到日志文件,日志保存在.ros/log目录下
respawn和required, 关于节点运行出错的处理,两个只能选一种
arg和param配合通过变量控制参数值,可以在执行launch时给定参数值,运行命令没有给定时使用默认值default设置的值
● C++ ,注意使用对应的参数类型
内部参数使用~ :比如rosRate为ssd_pkg的内部参数,其他包不能使用
全局参数加/ :version为全局参数,所有包可以加载
当然内部参数可以使用全路径方式指定:/ssd_pkg/rosRate
● python获取
version = rospy.get_param(‘/version’) # 获取全局参数带/
rete = rospy.get_param(‘~rosRate’)# 获取私有命名空间参数需要带~
config.yaml , 文件放在了test_pkg的同级目录,
注意
1.yaml使用空格设置层级,多少个空格没关系,保持一致即可,不能使用table
2. -开头组成列表(当然也可以使用[]),字典使用{}加键值对
3. 类型会自动识别字符串和数字,如果要指定为字符串需要用双引号
4. 重点说明:yaml缩进不能使用table