【ROS】ROS1编程速览
1、简述
很多项目已经转向ROS2,本人作为ROS小白从ROS1开始学起,但是不会深入学习ROS1,只一带而过。
下面只了解一些ROS1中的概念和基本编程接口。
ROS1中有两种通信模式:话题模式和服务模式,区别如下
2、话题模式
2.1 查看话题和消息
1)列出所有话题:rostopic list
~/workspace/ros$ rostopic list
/rosout
/rosout_agg
/turtle1/cmd_vel
/turtle1/color_sensor
/turtle1/pose
2)查看话题详细信息:rostopic info
和下面发布编程相关的话题:
~/workspace/ros$ rostopic info /turtle1/cmd_vel
Type: geometry_msgs/TwistSubscribers: * /turtlesim (http://laoer:43127/)
和下面订阅编程相关的话题:
$ rostopic info /turtle1/pose
Type: turtlesim/PosePublishers: * /turtlesim (http://laoer:43127/)
3)查看消息详细信息:rosmsg show
和下面发布编程相关的消息:
~/workspace/ros$ rosmsg show geometry_msgs/Twist
geometry_msgs/Vector3 linearfloat64 xfloat64 yfloat64 z
geometry_msgs/Vector3 angularfloat64 xfloat64 yfloat64 z
对应程序中的代码:
ros::Publisher turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);
geometry_msgs::Twist vel_msg;
vel_msg.linear.x = 0.5;
vel_msg.angular.z = 0.2;
turtle_vel_pub.publish(vel_msg);
和下面订阅编程相关的消息:
~/workspace/ros$ rosmsg show turtlesim/Pose
float32 x
float32 y
float32 theta
float32 linear_velocity
float32 angular_velocity
void poseCallback(const turtlesim::Pose::ConstPtr& msg)
{ROS_INFO("Turtle pose: x:%0.6f, y:%0.6f", msg->x, msg->y);
}
ros::Subscriber pose_sub = n.subscribe("/turtle1/pose", 10, poseCallback);
2.2、发布者
1)进入ROS1的工程目录
cd ~/workspace/ros/src/
2)创建功能包
~/workspace/ros/src$ catkin_create_pkg learning_topic roscpp rospy std_msgs geometry_msgs turtlesim
输出信息如下:
Created file learning_topic/CMakeLists.txt
Created file learning_topic/package.xml
Created folder learning_topic/include/learning_topic
Created folder learning_topic/src
Successfully created files in /home/laoer/workspace/ros/src/learning_topic. Please adjust the values in package.xml.
3)编辑源码
cd ~/workspace/ros
vi src/learning_topic/src/velocity_publisher.cpp
源码如下:
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>int main(int argc, char **argv)
{// ROS节点初始化ros::init(argc, argv, "velocity_publisher");// 创建节点句柄ros::NodeHandle n;// 创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10ros::Publisher turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);// 设置循环的频率ros::Rate loop_rate(10);int count = 0;while (ros::ok()){// 初始化geometry_msgs::Twist类型的消息geometry_msgs::Twist vel_msg;vel_msg.linear.x = 0.5;vel_msg.angular.z = 0.2;// 发布消息turtle_vel_pub.publish(vel_msg);ROS_INFO("Publsh turtle velocity command[%0.2f m/s, %0.2f rad/s]", vel_msg.linear.x, vel_msg.angular.z);// 按照循环频率延时loop_rate.sleep();}return 0;
}
4)修改CMakeLists.txt
~/workspace/ros$ vi src/learning_topic/CMakeLists.txt
添加需要编译生成的可执行文件规则和连接库
在## Build ##下添加
add_executable(velocity_publisher src/velocity_publisher.cpp)
target_link_libraries(velocity_publisher ${catkin_LIBRARIES})
5)编译
catkin_make
编译输出:
Base path: /home/laoer/workspace/ros
……
-- +++ processing catkin package: 'learning_topic'
-- ==> add_subdirectory(learning_topic)
……
[100%] Built target velocity_publisher
6)运行
在终端1中启动节点管理器:
roscore
在终端2中启动小乌龟节点:
rosrun turtlesim turtlesim_node
在终端3中先配置环境变量:
~/workspace/ros$ source devel/setup.bash
再启动发布者
~/workspace/ros$ rosrun learning_topic velocity_publisher
输出信息如下:
[ INFO] [1684324440.780789684]: Publsh turtle velocity command[0.50 m/s, 0.20 rad/s]
[ INFO] [1684324440.880933087]: Publsh turtle velocity command[0.50 m/s, 0.20 rad/s]
[ INFO] [1684324440.980945586]: Publsh turtle velocity command[0.50 m/s, 0.20 rad/s]
小乌龟将做圆形运动
2.3、订阅者
1)进入ROS1的工程目录
cd ~/workspace/ros/src/
2)创建功能包
已经创建过learning_topic,不需要再创建
~/workspace/ros/src$ catkin_create_pkg learning_topic roscpp rospy std_msgs geometry_msgs turtlesim
如果重复创建,将会输出如下信息,提示文件已存在:
catkin_create_pkg: error: File exists: /home/laoer/workspace/ros/src/learning_topic/CMakeLists.txt
3)编辑源码
cd ~/workspace/ros
vi src/learning_topic/src/pose_subscriber.cpp
#include <ros/ros.h>
#include "turtlesim/Pose.h"// 接收到订阅的消息后,会进入消息回调函数
void poseCallback(const turtlesim::Pose::ConstPtr& msg)
{// 将接收到的消息打印出来ROS_INFO("Turtle pose: x:%0.6f, y:%0.6f", msg->x, msg->y);
}int main(int argc, char **argv)
{// 初始化ROS节点ros::init(argc, argv, "pose_subscriber");// 创建节点句柄ros::NodeHandle n;// 创建一个Subscriber,订阅名为/turtle1/pose的topic,注册回调函数poseCallbackros::Subscriber pose_sub = n.subscribe("/turtle1/pose", 10, poseCallback);// 循环等待回调函数ros::spin();return 0;
}
4)修改CMakeLists.txt
~/workspace/ros$ vi src/learning_topic/CMakeLists.txt
添加需要编译生成的可执行文件规则和连接库
在## Build ##下添加
add_executable(pose_subscriber src/pose_subscriber.cpp)
target_link_libraries(pose_subscriber ${catkin_LIBRARIES})
5)编译
catkin_make
编译输出:
Base path: /home/laoer/workspace/ros
……
[100%] Built target pose_subscriber
6)运行
在终端1中启动节点管理器:
roscore
在终端2中启动小乌龟节点:
rosrun turtlesim turtlesim_node
在终端3中先配置环境变量:
~/workspace/ros$ source devel/setup.bash
再启动订阅者
~/workspace/ros$ rosrun learning_topic pose_subscriber
输出信息如下:
[ INFO] [1684373591.045027979]: Turtle pose: x:7.060666, y:10.029119
[ INFO] [1684373591.061111496]: Turtle pose: x:7.060666, y:10.029119
此时位置信息没有变化,可以运行2.1中发布者来改变位置信息
在终端4中先配置环境变量:
~/workspace/ros$ source devel/setup.bash
再启动发布者
~/workspace/ros$ rosrun learning_topic velocity_publisher
在终端3中可以看到位置信息已改变
[ INFO] [1684373649.013267506]: Turtle pose: x:3.651149, y:9.681684
[ INFO] [1684373649.028763094]: Turtle pose: x:3.645919, y:9.675630
2.4 节点结构
可以使用rqt_graph来打印当前的节点结构
2.5、话题消息
上面发布者和订阅者的示例中,都使用的是已经定义好的信息,如:
~/workspace/ros$ rosmsg show turtlesim/Pose
float32 x
float32 y
float32 theta
float32 linear_velocity
float32 angular_velocity
下面演示如何自定义话题信息
1)定义msg文件
进入话题工程目录
~/workspace/ros$ cd src/learning_topic/
创建保存消息文件的文件夹
~/workspace/ros/src/learning_topic$ mkdir msg
创建消息文件Person.msg
~/workspace/ros/src/learning_topic$ vi Person.msg
内容如下:
string name
uint8 age
uint8 sexuint8 unknown = 0
uint8 male = 1
uint8 female = 2
2)在package.xml中添加功能包依赖
在编译自定义消息时,需要依赖消息生成的依赖包:message_generation
在运行自定义消息时,需要依赖运行的依赖包:message_runtime
修改package.xml,将关于message_generation和message_runtime注释打开即可
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
3)C++代码
订阅者相关代码
~/workspace/ros$ vi src/learning_topic/src/person_subscriber.cpp
#include <ros/ros.h>
#include "learning_topic/Person.h"// 接收到订阅的消息后,会进入消息回调函数
void personInfoCallback(const learning_topic::Person::ConstPtr& msg)
{// 将接收到的消息打印出来ROS_INFO("Subcribe Person Info: name:%s age:%d sex:%d", msg->name.c_str(), msg->age, msg->sex);
}int main(int argc, char **argv)
{// 初始化ROS节点ros::init(argc, argv, "person_subscriber");// 创建节点句柄ros::NodeHandle n;// 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallbackros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback);// 循环等待回调函数ros::spin();return 0;
}
发布者相关代码:
~/workspace/ros$ vi src/learning_topic/src/person_publisher.cpp
#include <ros/ros.h>
#include "learning_topic/Person.h"int main(int argc, char **argv)
{// ROS节点初始化ros::init(argc, argv, "person_publisher");// 创建节点句柄ros::NodeHandle n;// 创建一个Publisher,发布名为/person_info的topic,消息类型为learning_topic::Person,队列长度10ros::Publisher person_info_pub = n.advertise<learning_topic::Person>("/person_info", 10);// 设置循环的频率ros::Rate loop_rate(1);int count = 0;while (ros::ok()){// 初始化learning_topic::Person类型的消息learning_topic::Person person_msg;person_msg.name = "Tom";person_msg.age = 18;person_msg.sex = learning_topic::Person::male;// 发布消息person_info_pub.publish(person_msg);ROS_INFO("Publish Person Info: name:%s age:%d sex:%d", person_msg.name.c_str(), person_msg.age, person_msg.sex);// 按照循环频率延时loop_rate.sleep();}return 0;
}
4)在CMakeLists.txt中添加编译选项
find_package(catkin REQUIRED COMPONENTS
……message_generation
)
add_message_files(FILESPerson.msg
)generate_messages(DEPENDENCIESstd_msgs
)
catkin_package(CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim message_runtime
)add_executable(person_publisher src/person_publisher.cpp)
target_link_libraries(person_publisher ${catkin_LIBRARIES})
add_dependencies(person_publisher ${PROJECT_NAME}_generate_messages_cpp)add_executable(person_subscriber src/person_subscriber.cpp)
target_link_libraries(person_subscriber ${catkin_LIBRARIES})
add_dependencies(person_subscriber ${PROJECT_NAME}_generate_messages_cpp)
5)编译
~/workspace/ros$ catkin_make
……
[ 46%] Generating Javascript code from learning_topic/Person.msg
[ 93%] Built target person_publisher
[100%] Built target person_subscriber
6)运行
在终端1中启动节点管理器:
~/workspace/ros$ roscore
在终端2中启动订阅者:
~/workspace/ros$ source devel/setup.bash
~/workspace/ros$ rosrun learning_topic person_subscriber
在终端3中启动发布者:
~/workspace/ros$ source devel/setup.bash
~/workspace/ros$ rosrun learning_topic person_publisher
终端2打印订阅者收到的信息:
[ INFO] [1684377584.196850906]: Subcribe Person Info: name:Tom age:18 sex:1
[ INFO] [1684377585.196714423]: Subcribe Person Info: name:Tom age:18 sex:1
终端3打印发布者发送的信息:
[ INFO] [1684377583.196183979]: Publish Person Info: name:Tom age:18 sex:1
[ INFO] [1684377584.196358219]: Publish Person Info: name:Tom age:18 sex:1
7)节点图很简单,如下:
3、服务模式
3.1 查看服务和数据
1)列出所有的服务
~/workspace/ros$ rosservice list
/clear
……
/spawn
……
2)查看服务(spawn产卵,即在界面中生成一个新的小乌龟)
~/workspace/ros$ rosservice info /spawn
Node: /turtlesim
URI: rosrpc://lesen-System-Product-Name:54203
Type: turtlesim/Spawn
Args: x y theta name
3)调用服务命令 rosservice call
下面的客户端示例的功能和这个命令类似,调用服务多产生几个小乌龟
~/workspace/ros$ rosservice call /spawn 3 7 8 haha
3.2、客户端
~/workspace/ros$ rosservice call /spawn 3 3 4 hah
1)进入ROS1的工程目录
cd ~/workspace/ros/src/
2)创建功能包
~/workspace/ros/src$ catkin_create_pkg learning_service roscpp rospy std_msgs geometry_msgs turtlesim
3)编辑源码
~/workspace/ros/src/learning_service/src$ vi turtle_spawn.cpp
#include <ros/ros.h>
#include <turtlesim/Spawn.h>int main(int argc, char** argv)
{// 初始化ROS节点ros::init(argc, argv, "turtle_spawn");// 创建节点句柄ros::NodeHandle node;// 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的serviceros::service::waitForService("/spawn");ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");// 初始化turtlesim::Spawn的请求数据turtlesim::Spawn srv;srv.request.x = 2.0;srv.request.y = 2.0;srv.request.name = "turtle2";// 请求服务调用ROS_INFO("Call service to spwan turtle[x:%0.6f, y:%0.6f, name:%s]", srv.request.x, srv.request.y, srv.request.name.c_str());add_turtle.call(srv);// 显示服务调用结果ROS_INFO("Spwan turtle successfully [name:%s]", srv.response.name.c_str());return 0;
};
4)修改CMakeLists.txt
添加需要编译生成的可执行文件规则和连接库
在## Build ##下添加
add_executable(turtle_spawn src/turtle_spawn.cpp)
target_link_libraries(turtle_spawn ${catkin_LIBRARIES})
5)编译
~/workspace/ros$ catkin_make
编译输出信息
[ 100%] Built target turtle_spawn
6)运行
在终端1中启动节点管理器:
~/workspace/ros$ roscore
在终端2中启动小乌龟:
rosrun turtlesim turtlesim_node
在终端3中启动客户端
~/workspace/ros$ source devel/setup.bash
~/workspace/ros$ rosrun learning_service turtle_spawn
输出信息如下:
[ INFO] [1684380205.381761081]: Call service to spwan turtle[x:2.000000, y:2.000000, name:turtle2]
[ INFO] [1684380205.401406453]: Spwan turtle successfully [name:turtle2]
在小乌龟界面将会出现两个小乌龟
3.3、服务端
1)进入ROS1的工程目录
cd ~/workspace/ros/src/
2)创建功能包
学习服务模式的功能包已经创建,这里不需要再运行
~/workspace/ros/src$ catkin_create_pkg learning_service roscpp rospy std_msgs geometry_msgs turtlesim
3)编辑源码
~/workspace/ros/src/learning_service/src$ vi turtle_command_server.cpp
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
#include <std_srvs/Trigger.h>ros::Publisher turtle_vel_pub;
bool pubCommand = false;// service回调函数,输入参数req,输出参数res
bool commandCallback(std_srvs::Trigger::Request &req,std_srvs::Trigger::Response &res)
{pubCommand = !pubCommand;// 显示请求数据ROS_INFO("Publish turtle velocity command [%s]", pubCommand==true?"Yes":"No");// 设置反馈数据res.success = true;res.message = "Change turtle command state!"return true;
}int main(int argc, char **argv)
{// ROS节点初始化ros::init(argc, argv, "turtle_command_server");// 创建节点句柄ros::NodeHandle n;// 创建一个名为/turtle_command的server,注册回调函数commandCallbackros::ServiceServer command_service = n.advertiseService("/turtle_command", commandCallback);// 创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);// 循环等待回调函数ROS_INFO("Ready to receive turtle command.");// 设置循环的频率ros::Rate loop_rate(10);while(ros::ok()){// 查看一次回调函数队列ros::spinOnce();// 如果标志为true,则发布速度指令if(pubCommand){geometry_msgs::Twist vel_msg;vel_msg.linear.x = 0.5;vel_msg.angular.z = 0.2;turtle_vel_pub.publish(vel_msg);}//按照循环频率延时loop_rate.sleep();}return 0;
}
4)修改CMakeLists.txt
添加需要编译生成的可执行文件规则和连接库
在## Build ##下添加
add_executable(turtle_command_server src/turtle_command_server.cpp)
target_link_libraries(turtle_command_server ${catkin_LIBRARIES})
5)编译
~/workspace/ros$ catkin_make
编译输出信息
……
[ 100%] Built target turtle_command_server
6)运行
在终端1中启动节点管理器:
~/workspace/ros$ roscore
在终端2中启动小乌龟:
rosrun turtlesim turtlesim_node
在终端3中启动服务器
~/workspace/ros$ source devel/setup.bash
~/workspace/ros$ rosrun learning_service turtle_spawn
在终端4中使用rosservice call来调用服务
rosservice call /turtle_command
终端3中的打印信息
[ INFO] [1684395192.110521550]: Ready to receive turtle command.
[ INFO] [1684395203.911182038]: Publish turtle velocity command [Yes]
[ INFO] [1684395245.311201634]: Publish turtle velocity command [No]
流程说明:
执行命令后将调用服务turtle_command,然后执行对应的回调函数,回调函数只控制pubCommand的真假值,主循环会根据pubCommand的真假来决定是否发布消息
注:小乌龟的运动是最终还是通过发布者Publisher,发布名为/turtle1/cmd_vel的主题topic,消息类型为控制小乌龟运动的消息geometry_msgs::Twist
节点关系如下:
3.4、自定义服务数据
3.4.1 定义服务数据
创建描述服务数据的srv文件
1)进入包目录
进入~/workspace/ros/src/learning_service中
cd ~/workspace/ros/src/learning_service
2)创建保存服务数据文件的目录srv
~/workspace/ros/src/learning_service$ mkdir srv
3)编辑服务数据文件Person.srv
~/workspace/ros/src/learning_service$ vi srv/Person.srv
内容如下,注意“—”不是省略号,是数据文件格式的一部分
string name
uint8 age
uint8 sexuint8 unknown = 0
uint8 male = 1
uint8 female = 2---
string result
4)在package.xml中添加功能包依赖
(和话题模式的自定义消息类似)
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
5)在CMakeLists.txt中添加编译选项
(和话题模式的自定义消息类似)
find_package(catkin REQUIRED COMPONENTS
……message_generation
)
add_service_files(FILESPerson.srv
)generate_messages(DEPENDENCIESstd_msgs
)catkin_package(CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim message_runtime
)
3.4.2 服务端
编辑服务端代码
~/workspace/ros/src/learning_service$ vi src/person_server.cpp
#include <ros/ros.h>
#include "learning_service/Person.h"// service回调函数,输入参数req,输出参数res
bool personCallback(learning_service::Person::Request &req,learning_service::Person::Response &res)
{// 显示请求数据ROS_INFO("Person: name:%s age:%d sex:%d", req.name.c_str(), req.age, req.sex);// 设置反馈数据res.result = "OK";return true;
}int main(int argc, char **argv)
{// ROS节点初始化ros::init(argc, argv, "person_server");// 创建节点句柄ros::NodeHandle n;// 创建一个名为/show_person的server,注册回调函数personCallbackros::ServiceServer person_service = n.advertiseService("/show_person", personCallback);// 循环等待回调函数ROS_INFO("Ready to show person informtion.");ros::spin();return 0;
}
3.4.3 客户端
编辑客户端代码
~/workspace/ros/src/learning_service$ vi src/person_client.cpp
#include <ros/ros.h>
#include "learning_service/Person.h"int main(int argc, char** argv)
{// 初始化ROS节点ros::init(argc, argv, "person_client");// 创建节点句柄ros::NodeHandle node;// 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的serviceros::service::waitForService("/show_person");ros::ServiceClient person_client = node.serviceClient<learning_service::Person>("/show_person");// 初始化learning_service::Person的请求数据learning_service::Person srv;srv.request.name = "Tom";srv.request.age = 20;srv.request.sex = learning_service::Person::Request::male;// 请求服务调用ROS_INFO("Call service to show person[name:%s, age:%d, sex:%d]", srv.request.name.c_str(), srv.request.age, srv.request.sex);person_client.call(srv);// 显示服务调用结果ROS_INFO("Show person result : %s", srv.response.result.c_str());return 0;
};
3.4.4 编译
1)在CMakeLists.txt中添加服务端、客户端相关的编译规则
add_executable(person_server src/person_server.cpp)
target_link_libraries(person_server ${catkin_LIBRARIES})
add_dependencies(person_server ${PROJECT_NAME}_gencpp)add_executable(person_client src/person_client.cpp)
target_link_libraries(person_client ${catkin_LIBRARIES})
add_dependencies(person_client ${PROJECT_NAME}_gencpp)
2)编译
~/workspace/ros$ catkin_make
编译输出
[ 80%] Built target person_server
[100%] Built target person_client
3.4.5 运行
在终端1中启动服务器:
~/workspace/ros$ rosrun learning_service person_server
在终端2中多次执行客户端,命令及打印信息如下:
~/workspace/ros$ rosrun learning_service person_client
[ INFO] [1684397359.830399671]: Call service to show person[name:Tom, age:20, sex:1]
[ INFO] [1684397359.831655045]: Show person result : OK
~/workspace/ros$ rosrun learning_service person_client
[ INFO] [1684397360.439275903]: Call service to show person[name:Tom, age:20, sex:1]
[ INFO] [1684397360.440764700]: Show person result : OK
终端1收到的信息如下:
[ INFO] [1684397334.962962627]: Ready to show person informtion.
[ INFO] [1684397351.231455028]: Person: name:Tom age:20 sex:1
[ INFO] [1684397358.320771056]: Person: name:Tom age:20 sex:1
[ INFO] [1684397359.144605775]: Person: name:Tom age:20 sex:1
4、参数
ROS 参 数( parameters )机制用于获取任何节点保存在参数服务器中的信息,类似全局变量。
方法是使用集中参数服务器( parameter server )维护一个变量集的值,包括整数、浮点数、字符串以及其他数据类型,每一个变量用一个较短的字符串标识 。由于允许节点主动查询其感兴趣的参数的值,它们适用于配置那些不会随时间频繁变更的信息。
4.1 rosparam
操作参数的命令
- rosparam set,设置参数
- rosparam get,获取参数
- rosparam load,从文件中加载参数
- rosparam dump,将参数写入文件
- rosparam delete,删除参数
- rosparam list,列出所有的参数
示例1:列出所有的参数
~/workspace/ros$ rosparam list
输出:
/rosdistro
/roslaunch/uris/host_lesen_system_product_name__46275
/rosversion
/run_id
/turtlesim/background_b
/turtlesim/background_g
/turtlesim/background_r
示例2:查看某个参数的值
~/workspace/ros$ rosparam get /turtlesim/background_b
输出:
255
4.2 自定义参数文件
1)进入ROS1的工程目录
cd ~/workspace/ros/src/
2)创建功能包
~/workspace/ros/src$ catkin_create_pkg learning_parameter roscpp rospy std_msgs geometry_msgs turtlesim
3)进入功能包目录
cd ~/workspace/ros/src/learning_parameter
4)创建保存参数文件的目录
mkdir config
5)编辑参数文件
~/workspace/ros/src/learning_parameter$ vi config/turtle_param.yaml
background_b: 255
background_g: 86
background_r: 69
rosdistro: 'melodic'
roslaunch:uris: {host_hcx_vpc__43763: 'http://hcx-vpc:43763/'}
rosversion: '1.14.3'
run_id: 077058de-a38b-11e9-818b-000c29d22e4d
4.3 参数操作示例
1)编辑源码
~/workspace/ros/src/learning_parameter$ vi src/parameter_config.cpp
注意:古月居的示例源码中background_r需要改为/turtlesim/background_r否则小乌龟不会改变背景色
ros::param::get("/turtlesim/background_r", red);
#include <string>
#include <ros/ros.h>
#include <std_srvs/Empty.h>int main(int argc, char **argv)
{int red, green, blue;// ROS节点初始化ros::init(argc, argv, "parameter_config");// 创建节点句柄ros::NodeHandle node;// 读取背景颜色参数ros::param::get("/turtlesim/background_r", red);ros::param::get("/turtlesim/background_g", green);ros::param::get("/turtlesim/background_b", blue);ROS_INFO("Get Backgroud Color[%d, %d, %d]", red, green, blue);// 设置背景颜色参数ros::param::set("/turtlesim/background_r", 255);ros::param::set("/turtlesim/background_g", 255);ros::param::set("/turtlesim/background_b", 255);ROS_INFO("Set Backgroud Color[255, 255, 255]");// 读取背景颜色参数ros::param::get("/turtlesim/background_r", red);ros::param::get("/turtlesim/background_g", green);ros::param::get("/turtlesim/background_b", blue);ROS_INFO("Re-get Backgroud Color[%d, %d, %d]", red, green, blue);// 调用服务,刷新背景颜色ros::service::waitForService("/clear");ros::ServiceClient clear_background = node.serviceClient<std_srvs::Empty>("/clear");std_srvs::Empty srv;clear_background.call(srv);sleep(1);return 0;
}
2)编译
~/workspace/ros$ catkin_make
输出:
[ 100%] Built target parameter_config
3) 运行
在终端1中启动节点管理器
roscore
在终端2中启动小乌龟
rosrun turtlesim turtlesim_node
在终端3中启动参数测试程序
~/workspace/ros$ source devel/setup.bash
~/workspace/ros$ rosrun learning_parameter parameter_config
打印输出:
[ INFO] [1684400198.204756094]: Get Backgroud Color[0, 14, -2147483648]
[ INFO] [1684400198.206004077]: Set Backgroud Color[255, 255, 255]
[ INFO] [1684400198.206633529]: Re-get Backgroud Color[255, 255, 255]
[ INFO] [1684400198.206846322]: waitForService: Service [/clear] has not been advertised, waiting...
5、坐标
5.1 命令示例
1)安装ros-melodic-turtle-tf
以小乌龟的坐标变换为例,需要先安装一个软件
$ sudo apt install ros-melodic-turtle-tf
2)终端1运行节点管理器
$ roscore
3)终端2运行两个小乌龟自动跟随的demo
$ roslaunch turtle_tf turtle_tf_demo.launch
4)终端3运行键盘控制节点
$ rosrun turtlesim turtle_teleop_key
5)坐标树查看
$ rosrun tf view_frames
输出信息
Listening to /tf for 5.0 seconds
Done Listening
dot - graphviz version 2.40.1 (20161225.0304)Detected dot version 2.40
frames.pdf generated
在当前目录下生成文件frames.pdf
6)坐标变换
$ rosrun tf tf_echo turtle1 turtle2
输出
At time 1684405017.779
- Translation: [0.000, 0.000, 0.000]
- Rotation: in Quaternion [0.000, 0.000, 0.979, 0.205]in RPY (radian) [-0.000, -0.000, 2.728]in RPY (degree) [-0.000, -0.000, 156.306]
At time 1684405018.514
- Translation: [0.000, 0.000, 0.000]
- Rotation: in Quaternion [0.000, 0.000, 0.979, 0.205]in RPY (radian) [-0.000, -0.000, 2.728]in RPY (degree) [-0.000, -0.000, 156.306]
……
7)rviz可视化工具查看坐标
$ rosrun rviz rviz -d `rospack find turtle_tf`/rviz/turtle_rviz.rviz
5.2 代码示例
1)进入ROS1的工程目录
cd ~/workspace/ros/src/
2)创建功能包
~/workspace/ros/src$ catkin_create_pkg learning_tf roscpp rospy tf turtlesim
3)编辑源码
tf广播器:
~/workspace/ros/src/learning_tf/src$ vi turtle_tf_broadcaster.cpp
#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <turtlesim/Pose.h>std::string turtle_name;void poseCallback(const turtlesim::PoseConstPtr& msg)
{// 创建tf的广播器static tf::TransformBroadcaster br;// 初始化tf数据tf::Transform transform;transform.setOrigin( tf::Vector3(msg->x, msg->y, 0.0) );tf::Quaternion q;q.setRPY(0, 0, msg->theta);transform.setRotation(q);// 广播world与海龟坐标系之间的tf数据br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", turtle_name));
}int main(int argc, char** argv)
{// 初始化ROS节点ros::init(argc, argv, "my_tf_broadcaster");// 输入参数作为海龟的名字if (argc != 2){ROS_ERROR("need turtle name as argument"); return -1;}turtle_name = argv[1];// 订阅海龟的位姿话题ros::NodeHandle node;ros::Subscriber sub = node.subscribe(turtle_name+"/pose", 10, &poseCallback);// 循环等待回调函数ros::spin();return 0;
};
tf监听器:
~/workspace/ros/src/learning_tf/src$ cat turtle_tf_listener.cpp
#include <ros/ros.h>
#include <tf/transform_listener.h>
#include <geometry_msgs/Twist.h>
#include <turtlesim/Spawn.h>int main(int argc, char** argv)
{// 初始化ROS节点ros::init(argc, argv, "my_tf_listener");// 创建节点句柄ros::NodeHandle node;// 请求产生turtle2ros::service::waitForService("/spawn");ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");turtlesim::Spawn srv;add_turtle.call(srv);// 创建发布turtle2速度控制指令的发布者ros::Publisher turtle_vel = node.advertise<geometry_msgs::Twist>("/turtle2/cmd_vel", 10);// 创建tf的监听器tf::TransformListener listener;ros::Rate rate(10.0);while (node.ok()){// 获取turtle1与turtle2坐标系之间的tf数据tf::StampedTransform transform;try{listener.waitForTransform("/turtle2", "/turtle1", ros::Time(0), ros::Duration(3.0));listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), transform);}catch (tf::TransformException &ex) {ROS_ERROR("%s",ex.what());ros::Duration(1.0).sleep();continue;}// 根据turtle1与turtle2坐标系之间的位置关系,发布turtle2的速度控制指令geometry_msgs::Twist vel_msg;vel_msg.angular.z = 4.0 * atan2(transform.getOrigin().y(),transform.getOrigin().x());vel_msg.linear.x = 0.5 * sqrt(pow(transform.getOrigin().x(), 2) +pow(transform.getOrigin().y(), 2));turtle_vel.publish(vel_msg);rate.sleep();}return 0;
};
4)配置CMakeLists.txt
add_executable(turtle_tf_broadcaster src/turtle_tf_broadcaster.cpp)
target_link_libraries(turtle_tf_broadcaster ${catkin_LIBRARIES})add_executable(turtle_tf_listener src/turtle_tf_listener.cpp)
target_link_libraries(turtle_tf_listener ${catkin_LIBRARIES})
5)编译
cd ~/workspace/ros
~/workspace/ros$ catkin_make
6)运行
终端1运行节点管理器
$ roscore
终端2运行小乌龟
$ rosrun turtlesim turtlesim_node
终端3运行广播1
$ rosrun learning_tf turtle_tf_broadcaster __name:=turtle1_tf_broadcaster /turtle1
终端4运行广播2
$ rosrun learning_tf turtle_tf_broadcaster __name:=turtle2_tf_broadcaster /turtle2
终端5运行监听
$ rosrun learning_tf turtle_tf_listener
终端6运行键盘控制
$ rosrun turtlesim turtle_teleop_key
7)效果
效果和命令示例一样
6、launch批量启动节点
6.1 说明
roslaunch可以实现自动启动ROS Master、通过XML文件实现多个节点的配置和启动
6.2 创建launch功能包
1)进入ROS1的工程目录
cd ~/workspace/ros/src/
2)创建功能包
~/workspace/ros/src$ catkin_create_pkg learning_launch
6.3 launch文件格式
roslaunch需要加载XML文件来读取各个节点,格式如下:
<launch><node pkg="learning_topic" type="person_subscriber" name="talker" output="screen" /><node pkg="learning_topic" type="person_publisher" name="listener" output="screen" />
</launch>
1)launch:根元素
2)node:节点
pkg:节点所在的功能包名称
type:可执行文件名称
name:执行程序运行时的名称
output:打印输出
3)include:launch文件嵌套
6.4 执行命令
~/workspace/ros$ roslaunch learning_launch simple.launch
输出:
……
NODES
/
listener (learning_topic/person_publisher)
talker (learning_topic/person_subscriber)
ROS_MASTER_URI=http://localhost:11311
process[talker-1]: started with pid [6649]
process[listener-2]: started with pid [6655]
[ INFO] [1684408582.094373243]: Publish Person Info: name:Tom age:18 sex:1
[ INFO] [1684408583.094561611]: Publish Person Info: name:Tom age:18 sex:1
[ INFO] [1684408583.095011334]: Subcribe Person Info: name:Tom age:18 sex:1
[ INFO] [1684408584.094552704]: Publish Person Info: name:Tom age:18 sex:1
[ INFO] [1684408584.094849697]: Subcribe Person Info: name:Tom age:18 sex:1
7、可视化工具
7.1 rqt:rqt系列工具集
7.1 rqt_console:日志输出工具
7.2 rqt_graph:节点图
7.3 rqt_plot:数据绘制
7.4 rqt_image_view:图像渲染工具
不知道怎么安装
7.5 rqt_bag
7.6 Rviz:三维可视化工具
7.7 Gazebo:三维物理仿真平台
7.8 rqt_shell终端
rqt_shell
7.9 rqt_multiplot:可视化多个2D图中的数值
7.10 rqt_logger_level:配置 ROS 节点的日志级别
7.11 rqt_paramedit:编辑参数服务
7.12 rqt_py_trees:可视化py_trees行为树
行为树:用来让机器人实现复杂任务
7.13 rqt_dep:查看ROS包依赖
相关文章:

【ROS】ROS1编程速览
1、简述 很多项目已经转向ROS2,本人作为ROS小白从ROS1开始学起,但是不会深入学习ROS1,只一带而过。 下面只了解一些ROS1中的概念和基本编程接口。 ROS1中有两种通信模式:话题模式和服务模式,区别如下 2、话题模式 …...

探索智能化:TOOM解析未来稿件校验系统的技术进展与应用展望
在信息时代,随着大数据、人工智能和自然语言处理等技术的快速发展,稿件校验系统正朝着智能化的方向迈进。智能化的稿件校验系统能够更准确、高效地检测虚假信息、抄袭行为以及提升文章质量。本文将探讨智能化稿件校验系统的技术进展与应用展望࿰…...
Java程序员从青铜到王者,不同段位的薪资和技能变化
想要薪资高,段位就得跟上,对于Java程序员来说,从青铜到王者,需要经历多个阶段,每个阶段需要掌握的技能都不一样。 今天,我们一起来看看每个段位都有什么特点、需要具备哪些“大杀”技能,也看看…...

tinyWebServer 学习笔记——二、HTTP 连接处理
文章目录 一、基础知识1. epoll2. 再谈 I/O 复用3. 触发模式和 EPOLLONESHOT4. HTTP 报文5. HTTP 状态码6. 有限状态机7. 主从状态机8. HTTP_CODE9. HTTP 处理流程 二、代码解析1. HTTP 类2. 读取客户数据2. epoll 事件相关3. 接收 HTTP 请求4. HTTP 报文解析5. HTTP 请求响应 …...

深入浅析Linux Perf 性能分析工具及火焰图
Perf Event 子系统 Perf 是内置于 Linux 内核源码树中的性能剖析(profiling)工具。它基于事件采样的原理,以性能事件为基础,支持针对处理器相关性能指标与操作系统相关性能指标的性能剖析。可用于性能瓶颈的查找与热点代码的定位…...
java关键术语
java具有11个关键的术语,这些术语是从java的设计者所编写的白皮书中摘取,这些术语分别为:简单性、面向对象、分布式、健壮性、安全性、体系结构中立、可移植性、解释型、高性能、多线程、多态性。以下开始我们将逐一解说这些术语。 一、简单性 Java是C++语法的纯净版本,剔…...
1. 两数之和【简单】
题目 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺…...

《编码——隐匿在计算机软硬件背后的语言》精炼——第17章(自动操作)
夫道成于学而藏于书,学进于振而废于穷。 文章目录 完善加法器加入代码的加法器扩大加数范围自由调用地址的加法器合并代码RAM和数据RAMJump指令硬件实现条件Jump指令零转移的硬件实现条件Jump指令的例子 总结 完善加法器 我们在第14章介绍了一个可以进行连加的加法…...

用Colab免费部署AI绘画云平台Stable Diffusion webUI
Google Colab 版的 Stable Diffusion WebUI 1.4 webui github 地址:https://github.com/sd-webui/stable-diffusion-webui 平台搭建 今天就来交大家如果来搭建和使用这个云平台。 第一步: 打开链接 https://colab.research.google.com/github/altryne/sd-webu…...

R.I.P,又一位程序员巨佬——左耳朵耗子陨落
震惊!谣言吧!求辟谣!默哀! 左耳朵耗子,在程序员这个群体里应该属于 GOAT 的存在了,虽然每个人心目中都有自己的 GOAT,但耗子叔的影响力可以说是有目共睹。 我也是在技术群刷到这张图片的&#…...

捷威信keithley吉时利2410数字源表 销售回收KEITHLEY2470新款源表
吉时利Keithley 2410 /2470高压源表/数字源表 产品概览 Keithley 2410 高压源表专为需要紧密耦合源和测量的测试应用而设计。Keithly 2410 提供精密电压和电流源以及测量功能。它既是高度稳定的直流电源,又是真正的仪器级 5-1/2 数字万用表。电源特性包括低噪声、…...
第二十九回:如何给ListView添加分隔线
文章目录 概念介绍添加方法使用属性装饰器 示例代码经验总结: 我们在上一章回中介绍了多种创建ListView的方式,本章回中将介绍" 如何给ListView添加分隔线".闲话休提,让我们一起Talk Flutter吧。 概念介绍 我们在这里说的分隔线也叫Divider,…...
用友 LRP计划维护视图
select planlotnumber 计划单号, demandId 自动编号, PartId 物料Id , sotype 单据类型(1:销售/2:预测), sodid 销售订单明细Id , socode 销售订单单号 , soseq 销售订单行号, PlanCode 计划单号 , DueDate 完工日期 , StartDate 开工日期 , UnitCode 主计量单位, C…...

数组--part 5--螺旋矩阵(力扣59/54)(剑指offer 29)
文章目录 基本算法思想leetcode 59 螺旋矩阵 IIleetcode 54 螺旋矩阵剑指Offer 29 顺时针打印矩阵 基本算法思想 建议先去把题目看了,再来思考相关的代码。 错误的想法:实际上这种题型并不存在算法,只涉及到模拟,但是模拟难度并…...

加密解密软件VMProtect入门使用教程(九)许可制度之许可系统功能
VMProtect是新一代软件保护实用程序。VMProtect支持德尔菲、Borland C Builder、Visual C/C、Visual Basic(本机)、Virtual Pascal和XCode编译器。 同时,VMProtect有一个内置的反汇编程序,可以与Windows和Mac OS X可执行文件一起…...

MySQL基础-事务详解
本文主要介绍MySQL事务 文章目录 前言事务定义事务四大特性(ACID) 事务操作事务并发问题事务隔离级别 前言 参考链接: 链接1链接2 事务定义 事务是一组操作的集合,他是一个不可分割的工作单位,事务会把所有的操作作…...

python 读写csv文件方法
csv是一种结构化文件,可以将文本转化成矩阵的形式,方便程序读取和处理。下面来介绍一下使用 python读写 csv文件的方法: 1.首先需要使用 pip安装 python包,然后将 csv文件解压到一个文件夹下 2.使用 pip安装 python包,…...

命令行更新Windows
命令行更新Windows powershell命令行更新安装 Windows Update module for Windows Powershell连接到 Windows Update 服务器并下载更新安装下载好的 Windows Update 更新 cmd执行Windows update更新检查更新下载 Windows Update 更新安装更新安装更新后重新启动设备 win10以下版…...
lwIP 多线程注意事项
关于 lwIP 多线程的总结: lwIP 内核不是线程安全的。如果在多线程环境中使用 lwIP,必须使用高层次的 Sequential 或 socket API。使用 raw API 时,需要自己保护好应用程序和协议栈核心代码。在无操作系统环境中使用 raw API: 使用…...
工业革命的本质是动力革命:人类使用能量的水平得到了飞跃(蒸汽动力取代畜力和水力,机械代替人工。)【工业革命的诞生是能量富余的结果】
文章目录 引言I 用能量守恒方式看工业革命的影响1.1 中学物理能量守恒1.2 看清历史事件的影响1.3 工业革命的意义1.4 透过现象看本质的方法II 工业革命的本质2.1 动力革命2.2 多余的能量造就了工业革命引言 人类文明进步的目的是改善人们的生活,任何文明都以养活更多的人口为…...

传统业务对接AI-AI编程框架-Rasa的业务应用实战(番外篇2)-- Rasa 训练数据文件的清理
经过我的【传统业务对接AI-AI编程框架-Rasa的业务应用实战】系列 1-6 的表述 已经实现了最初的目标:将传统平台业务(如发票开具、审核、计税、回款等)与智能交互结合,通过用户输入提示词或语音,识别用户意图和实体信…...
kubeadm安装k8s
1、环境准备 1.1、升级系统内核 参考另一篇文章:https://blog.csdn.net/u012533920/article/details/148457715?spm1011.2415.3001.5331 1.2、设置Hostname cat <<EOF > /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhos…...

NeRF 技术深度解析:原理、局限与前沿应用探索(AI+3D 产品经理笔记 S2E04)
引言:光影的魔法师——神经辐射场概览 在前三篇笔记中,我们逐步揭开了 AI 生成 3D 技术的面纱:从宏观的驱动力与价值(S2E01),到主流技术流派的辨析(S2E02),再到实用工具的…...
使用Caddy在Ubuntu 22.04上配置HTTPS反向代理
使用Caddy在Ubuntu 22.04上配置HTTPS反向代理(无域名/IP验证+密码保护) 一、 环境说明 环境说明:测试环境,生产环境请谨慎OS: Ubuntu 22.04.1 LTSCaddy版本:v2.10.0服务器IP: 192.168.3.88(内网)公网IP: 10.2.3.11(测试虚拟)代理端口: 9080后端服务: http://192.168.3…...
Qwen大语言模型里,<CLS>属于特殊的标记:Classification Token
Qwen大语言模型里,<CLS>属于特殊的标记:Classification Token 目录 Qwen大语言模型里,<CLS>属于特殊的标记:Classification Token功能解析工作机制应用场景举例说明技术要点在自然语言处理(NLP)领域 都是<CLS> + <SEP>吗?一、CLS和SEP的作用与常见用法1. **CLS标…...

C++11新增重要标准(下)
前言 一,forward(完美转发) 二,可变参数模板 三,emplace系列接口 四,新增类功能 五,default与delete 六,lambda表达式 七,包装器 八,bind 在C11中新增…...

关于单片机的基础知识(一)
成长路上不孤单😊😊😊😊😊😊 【14后😊///计算机爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于单片机基础知识的相关内容…...

基于React + FastAPI + LangChain + 通义千问的智能医疗问答系统
📌 文章摘要: 本文详细介绍了如何在前端通过 Fetch 实现与 FastAPI 后端的 流式响应通信,并支持图文多模态数据上传。通过构建 multipart/form-data 请求,配合 ReadableStream 实时读取 AI 回复内容,实现类似 ChatGPT…...
Python Copilot【代码辅助工具】 简介
粉丝爱买鳕鱼肠深海鳕鱼肉鱼肉香肠盼盼麦香鸡味块卡乐比(Calbee)薯条三兄弟 独立小包美丽雅 奶茶杯一次性饮料杯好时kisses多口味巧克力糖老金磨方【黑金系列】黑芝麻丸郑新初网红郑新初烤鲜牛肉干超人毛球修剪器去球器剃毛器衣服去毛器优惠券宁之春 红黑…...
Flask 基础与实战概述
一、Flask 基础知识 什么是 Flask? Flask 是一个基于 Python 的轻量级 Web 框架(微框架)。 特点:核心代码简洁,给予开发者更多选择空间。 与 Django 对比: Django 创建空项目生成多个文件,Flask 仅需一个文件即可实现简单应用(如 "Hello, World!")。 Flask …...