Chapter3 ROS通信机制----进阶篇(Ⅰ)ROS常用函数及自定义头文件源文件的调用

本文主要是介绍Chapter3 ROS通信机制----进阶篇(Ⅰ)ROS常用函数及自定义头文件源文件的调用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、常用API

1.1 C++部分

1.1.1 init函数

1.1.2 话题与服务相关对象

1.1.3 回旋函数

1.1.4 时间

1.1.5 其他函数

1.2 python部分

1.2.1 init_code 函数

1.2.2 话题服务对象

1.2.3 回旋函数

1.2.4 时间

1.2.5 其他函数

二、ROS中的头文件和源文件

2.1 本节内容介绍

2.2 自定义头文件的调用

2.2.1 需求及流程

2.2.2 自定义头文件的调用

2.3 自定义源文件的调用 

2.3.1 需求及流程

 2.3.2 C++自定义源文件的使用

 2.3.3 python自定义源文件的使用 


一、常用API

1.1 C++部分

1.1.1 init函数

Ⅰ.新建一个功能包plumbing_apis,依赖包为roscpp rospy std_msgs,新建文件demo01_apis_pub.cpp

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
/*发布方实现1.包含头文件2.初始化ros节点3.创建节点句柄4.创建发布者对象5.编写发布逻辑并发布数据
*/
int main(int argc ,char * argvs[])
{setlocale(LC_ALL,"");ros::init(argc,argvs,"erguizi",ros::init_options::AnonymousName);  //节点名称为erguiziros::NodeHandle nh;ros::Publisher pub = nh.advertise<std_msgs::String>("house",10);    //话题为房子,队列中最多缓存十条数据//5-1.先创建没有被发布的信息std_msgs::String msg;ros::Rate rate(10);        //10hzint count = 0;//5-2.编写循环,循环中发布数据ros::Duration(3).sleep();   //程序执行到这里休眠三秒钟while(ros::ok())    //只要节点活着,则循环继续{count ++;//实现字符串拼接std::stringstream ss;ss << "hello --->"<<count;msg.data = ss.str();//msg.data = "hello";pub.publish(msg);//添加日志ROS_INFO("发布的数据是:%s",ss.str().c_str());rate.sleep();                        //睡觉0.1s}return 0;
}

Ⅱ.ros::init参数介绍

argc:封装实参的个数(传入n个参数,argc = 参数+1,因为第一个参数是文件名)

argv:封装参数的指针数组(字符串类型)

name:为节点命名,是字符串类型的,有唯一性

option:节点启动选项

返回值:void

Ⅲ.使用

①argc 与 argv 的使用

如果按照ros中的特定格式传入实参,那么ROS可以加以使用,比如可以设置全局参数,给节点重命名

启动节点为参数服务器设置参数,采用一个下划线一个参数名冒号等号具体值,这时下划线length就被传递给argv了

liuhongwei@liuhongwei-virtual-machine:~/demo03_ws$ clear
liuhongwei@liuhongwei-virtual-machine:~/demo03_ws$ source ./devel/setup.bash 
liuhongwei@liuhongwei-virtual-machine:~/demo03_ws$ rosrun plumbing_apis demo01_apis_pub _length:=2

这时查看参数服务器发现多了length参数,再查看其值

liuhongwei@liuhongwei-virtual-machine:~$ rosparam list
/erguizi/length
liuhongwei@liuhongwei-virtual-machine:~$ rosparam get /erguizi/length 
2

②options的使用:
节点名称要保证唯一性,会导致一个问题:同一个节点不能重复启动,如果重复启动之前启动的会被关闭

需求:特定场景下需要一个节点多次启动且能正常运行,怎么办呢?使用options参数

ros::init(argc,argvs,"erguizi",ros::init_options::AnonymousName);  //节点名称为erguizi

这样就能重复启动了:功能实现如下

1.1.2 话题与服务相关对象

1.在roscpp中,话题和服务相关对象一般由 NodeHandle 创建。

2. advertise函数

Ⅰ.作用:创建发布者对象

ros::Publisher pub = nh.advertise<std_msgs::String>("house",10); 

Ⅱ.模板:被发布消息的对象 <std_msgs::String>

Ⅲ.参数:

①参数名称及作用

话题名称:house,string类型

队列长度:缓冲区最多可容纳数据

latch(可选):如果设置为true,会保存发送方的最后一条信息,并且新的连接对象链接到发布方时,发布方会将这条信息发送给订阅者,默认值为false即不会保存

②应用场景:即latch设置为true的作用

场景机器人导航时,需要一个静态地图,地图通过发布方来发布,如何设置发布频率,如果是一秒钟10HZ,地图是静态长期不会变,一个解决方案是只发一次地图信息,但将发布对象的latch对象值设置为true,这样意味着当订阅者链接到话题时候,就会将地图发送给订阅方。

code:对比两种不同方式

③原来的代码(没有设置latch)

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>int main(int argc ,char * argvs[])
{setlocale(LC_ALL,"");ros::init(argc,argvs,"erguizi",ros::init_options::AnonymousName);  ros::NodeHandle nh;ros::Publisher pub = nh.advertise<std_msgs::String>("house",10);    std_msgs::String msg;ros::Rate rate(10);        int count = 0;ros::Duration(3).sleep();   while(ros::ok())    {count ++;std::stringstream ss;ss << "hello --->"<<count;msg.data = ss.str();if(count<=10){pub.publish(msg);ROS_INFO("发布的数据是:%s",ss.str().c_str());}rate.sleep();                     }return 0;
}

运行,发现发布者只能发送十条数据,这时调用订阅方,实验结果如下

发现订阅方订阅不到任何数据

④测试的代码(设置latch为true)

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>int main(int argc ,char * argvs[])
{setlocale(LC_ALL,"");ros::init(argc,argvs,"erguizi",ros::init_options::AnonymousName);  ros::NodeHandle nh;ros::Publisher pub = nh.advertise<std_msgs::String>("house",10,true);    std_msgs::String msg;ros::Rate rate(10);        int count = 0;ros::Duration(3).sleep();   while(ros::ok())    {count ++;std::stringstream ss;ss << "hello --->"<<count;msg.data = ss.str();if(count<=10){pub.publish(msg);ROS_INFO("发布的数据是:%s",ss.str().c_str());}rate.sleep();                     }return 0;
}

运行,发现发布者只能发送十条数据,这时调用订阅方,实验结果如下

 发现订阅方可以订阅到最后一条数据

Ⅲ.返回值: ros::Publisher型数据,用来创建发布方

1.1.3 回旋函数

1. 在ROS程序中,频繁的使用了 ros::spin() 和 ros::spinOnce() 两个回旋函数,可以用于处理回调函数。

2.spin与spinonce区别

相同点:二者都用于处理回调函数;

不同点:ros::spin() 是进入了循环执行回调函数,而 ros::spinOnce() 只会执行一次回调函数(没有循环),在 ros::spin() 后的语句不会执行到,而 ros::spinOnce() 后的语句可以执行。

1.1.4 时间

1.ROS中时间相关的API是极其常用,比如:获取当前时刻、持续时间的设置、执行频率、休眠、定时器...都与时间相关。

2.时刻

Ⅰ.作用:获取时刻,或是设置指定时刻

Ⅱ.实现--获取时刻:

①时间相关的头文件已经在 ros.h 中包含,无须包含其他头文件

②API:ros::Time::now() ;     返回值类型为 ros::Time 类型 值为距离1970/01/01的时间

ros::Time rightnow =  ros::Time::now();

③参考代码Ⅰ

int main(int argc, char  *argv[])
{setlocale(LC_ALL,"");ros::init(argc,argv,"helllo_time");ros::NodeHandle nh;//now函数会将当前时刻封装并返回,当前时刻:now被调用执行的时刻ros::Time rightnow =  ros::Time::now();//将离参考系的时间转换成秒ROS_INFO("当前时刻%.2f",rightnow.toSec());ROS_INFO("当前时刻%d",rightnow.sec);return 0;
}

④运行结果Ⅰ

liuhongwei@liuhongwei-virtual-machine:~/demo03_ws$ rosrun plumbing_apis demo02_apis_time 
[ INFO] [1649842275.104207240]: 当前时刻1649842275.10
[ INFO] [1649842275.105031117]: 当前时刻1649842275

Ⅲ.实现--设置指定时刻:

//设置时间为距离1970/01/01 过去20秒钟 + 2426655纳秒
ros::Time t1(20,2426655);
ROS_INFO("当前时刻%.2f",t1.toSec());
[ INFO] [1649842603.712080612]: 当前时刻20.00

3.持续时间(程序休眠)

Ⅰ.需求:程序中停顿5秒

Ⅱ.步骤:

①创建持续时间对象

②休眠操作

Ⅲ.code

ros::Duration du(4.5);   //有一个持续时间,持续时间封装了4.5秒
du.sleep();

4.时间运算

Ⅰ.需求:已知程序开始运行的时刻和程序运行的时间,求程序运行结束的时刻

Ⅱ.步骤:

①获取开始执行的时刻

②调用duration模拟运行时间

③计算结束时刻

Ⅲ.code

ros::Time begin = ros::Time::now();
ros::Duration du1(5);
ros::Time stop = begin + du1;
ROS_INFO("开始的时刻:%.2f",begin.toSec());
ROS_INFO("结束的时刻:%.2f",stop.toSec());

 Ⅳ.运行

[ INFO] [1649849886.072840085]: 开始的时刻:1649849886.07
[ INFO] [1649849886.072916669]: 结束的时刻:1649849891.07

Ⅴ.code2

ros::Duration du3 = begin - stop ;
ROS_INFO("时刻相减:%.2f",du3.toSec());

Ⅵ.运行

[ INFO] [1649850600.163914520]: 时刻相减:-5.00

Ⅶ.此外,持续时间也可相加,即 du3 du1之间可以运算。

5.设置运行频率

rosRate rate(1);    rate.sleep()  实现程序休眠 

 6.定时器

Ⅰ.需求:ROS 中内置了专门的定时器,可以实现与 ros::Rate 类似的效果

Ⅱ.code:输出结果,每隔一秒输入一行--------

void cb(const ros::TimerEvent & event)
{ROS_INFO("----------------------");
}//需求:每隔一秒钟 后台输出一段文本
ROS_INFO("-----------------------------定时器-----------------------------------");
//时间间隔 回调函数 false(定时器是否一次性,如果为true,一秒钟执行一次回调函数,只执行一次,false每隔一秒执行一次) 
//true 定时器是否自动启动
ros::Timer timer = nh.createTimer(ros::Duration(1),cb);
ros::spin();

Ⅲ.定时器其他参数

①参数3,默认false,改成true,执行结果:输出一行-------------

void cb(const ros::TimerEvent & event)
{ROS_INFO("----------------------");
}//需求:每隔一秒钟 后台输出一段文本
ROS_INFO("-----------------------------定时器-----------------------------------");
//时间间隔 回调函数 false(定时器是否一次性,如果为true,一秒钟执行一次回调函数,只执行一次,false每隔一秒执行一次) 
//true 定时器是否自动启动
ros::Timer timer = nh.createTimer(ros::Duration(1),cb,true);
ros::spin();

②参数4,是否自动启动,默认true,改成false,则定时器不自动启动,输出结果

void cb(const ros::TimerEvent & event)
{ROS_INFO("----------------------");
}//需求:每隔一秒钟 后台输出一段文本
ROS_INFO("-----------------------------定时器-----------------------------------");
//时间间隔 回调函数 false(定时器是否一次性,如果为true,一秒钟执行一次回调函数,只执行一次,false每隔一秒执行一次) 
//true 定时器是否自动启动
ros::Timer timer = nh.createTimer(ros::Duration(1),cb,true,false);
ros::spin();

如何启动?调用 timer.start();  手动启动

1.1.5 其他函数

1.节点退出的情况

  • 节点接收到了关闭信息,比如常用的 ctrl + c 快捷键就是关闭节点的信号;
  • 同名节点启动,导致现有节点退出;
  • 程序中的其他部分调用了节点关闭相关的API(C++中是ros::shutdown(),python中是rospy.signal_shutdown())

2.ROS日志的五个级别

  • DEBUG(调试):只在调试时使用,此类消息不会输出到控制台;
  • INFO(信息):标准消息,一般用于说明系统内正在执行的操作;
  • WARN(警告):提醒一些异常情况,但程序仍然可以执行;
  • ERROR(错误):提示错误信息,此类错误会影响程序运行;
  • FATAL(严重错误):此类错误将阻止节点继续运行。

3.code

ROS_DEBUG("hello,DEBUG"); //不会输出
ROS_INFO("hello,INFO"); //默认白色字体
ROS_WARN("Hello,WARN"); //默认黄色字体
ROS_ERROR("hello,ERROR");//默认红色字体
ROS_FATAL("hello,FATAL");//默认红色字体

1.2 python部分

1.2.1 init_code 函数

1.参数用途

Ⅹ.案例所用代码

#! /usr/bin/env python
#encoding:utf-8
import rospy
from std_msgs.msg import Stringif __name__ == "__main__":#2.初始化 ROS 节点:命名(唯一)rospy.init_node("lhw")#3.实例化 发布者 对象pub = rospy.Publisher("car",String,queue_size=10)#4.组织被发布的数据,并编写逻辑发布数据msg = String()  #创建 msg 对象msg_front = "hello 你好"count = 0  #计数器 # 设置循环频率rate = rospy.Rate(1)while not rospy.is_shutdown():#拼接字符串msg.data = msg_front + str(count)pub.publish(msg)rate.sleep()rospy.loginfo("写出的数据:%s",msg.data)count += 1

Ⅰ.各个参数解释

参数① name:设置节点名称

参数②argv:默认值为None,封装节点调用时传递的参数

参数③anonymous:默认值为False,可以为节点生成随机名称,解决节点名称重名问题

Ⅱ.使用--argv

有了argv,可以按照ROS指定的语法格式传参,ROS可以解析并加以使用

liuhongwei@liuhongwei-virtual-machine:~/demo03_ws$ rosrun plumbing_apis demo01pis_pub _a:=10000

这时ROS会解析命令行,并且转化为参数服务器里面的变量

 Ⅲ.使用--anonymous

如果一个节点多次使用,则可使用此参数

    rospy.init_node("lhw",anonymous=True)

1.2.2 话题服务对象

1.发布者对象Publisher的参数

Ⅰ.latch参数,默认值为false。如果为true,发布方的最后一条信息会被保存且新的订阅者链接到发布方时会将最后一条信息发送给订阅者

Ⅱ.code:

#! /usr/bin/env python
#encoding:utf-8
import rospy
from std_msgs.msg import Stringif __name__ == "__main__":#2.初始化 ROS 节点:命名(唯一)rospy.init_node("lhw",anonymous=True)#3.实例化 发布者 对象pub = rospy.Publisher("car",String,queue_size=10,latch=True)#4.组织被发布的数据,并编写逻辑发布数据msg = String()  #创建 msg 对象msg_front = "hello 你好"count = 0  #计数器 # 设置循环频率rate = rospy.Rate(1)while not rospy.is_shutdown():count += 1#拼接字符串if count <= 10:msg.data = msg_front + str(count)pub.publish(msg)rate.sleep()rospy.loginfo("写出的数据:%s",msg.data)

1.2.3 回旋函数

1.2.4 时间

1.获取当前时间

Ⅰ.code1

    time_now =  rospy.Time.now()rospy.loginfo("当前时刻:%.2f",time_now.to_sec())rospy.loginfo("当前时刻:%d",time_now.secs)

Ⅱ.演示

liuhongwei@liuhongwei-virtual-machine:~/demo03_ws$ rosrun plumbing_apisemo02_apis_time.py
[INFO] [1649854741.179435]: 当前时刻:1649854741.18
[INFO] [1649854741.181424]: 当前时刻:1649854741

Ⅲ.code2

    #将时间封装成time对象,此时距离时间原点距离100.5s + 多少纳秒time1 = rospy.Time(100.5) time2 = rospy.Time(100,3255555)rospy.loginfo("指定时间1 :%.2f",time1.to_sec())rospy.loginfo("指定时间2 :%.2f",time2.to_sec())

Ⅳ.演示

[INFO] [1649855025.249242]: 指定时间1 :100.50
[INFO] [1649855025.249948]: 指定时间2 :100.00

2.程序中停顿5秒

    #让程序执行中停顿五秒钟#1.封装一个持续时间对象rospy.loginfo("休眠前------------------------------------------")#5秒 +  x纳秒,也可以只用一个秒参数du = rospy.Duration(5,0)#2.再将持续时间休眠rospy.sleep(du)rospy.loginfo("休眠后------------------------------------------")

3.获取程序开始的时刻,且已知持续运行的时间,计算程序结束的时间

    #需求:获取程序开始执行的时刻,且已知的运行时间,计算程序结束的时间#1.先获取一个时刻t1 = rospy.Time.now()#2.设置一个持续时间du1 = rospy.Duration(5)#3.将t2 =t1 + du1t2 = t1 + du1rospy.loginfo("开始时刻:%.2f",t1.to_sec())rospy.loginfo("结束时刻:%.2f",t2.to_sec())

4.创建定时器,实现类似于 ros::Rate 的功能 

Ⅰ.设置运行频率

rate = rospy.Rate(0.5)
while not rospy.is_shutdown():rate.sleep()rospy.info("-------------")

Ⅱ.定时器

①code

def domsg(event):rospy.loginfo("-----------------------------")#需求:创建一个定时器,功能类似于rospy.Rate的功能#函数参数#1.持续时间 类型为rospy.Duration类型#2.回调函数#3.是否一次性 默认值false#4.timer = rospy.Timer(rospy.Duration(2),domsg,False)rospy.spin()

②运行结果:每隔两秒钟打印一行-------

[ INFO] [1649899413.898624981]: ----------------------
[ INFO] [1649899414.899210587]: ----------------------
[ INFO] [1649899415.899310332]: ----------------------
[ INFO] [1649899416.898880850]: ----------------------
[ INFO] [1649899417.899059914]: ----------------------
[ INFO] [1649899418.898944819]: ----------------------
[ INFO] [1649899419.898597456]: ----------------------

③若第三个参数为true,则只会执行一行--------------

④回调函数的参数是 Event 类型的,其中有一个方法是 current_real 方法

    rospy.loginfo("调用回调函数的时刻:%.2f",event.current_real.to_sec())

1.2.5 其他函数

1.节点判断函数

Ⅰ.isshutdown

返回的是bool值,如果节点没被关闭,返回true,反之返回false,可以用来检测节点的关闭状态

Ⅱ. 手动关闭节点 rospy.signal_shutdown("关闭节点") 可以手动关闭节点,参数为字符串是提示输出的信息。

Ⅲ.节点关闭之后执行回调函数:rospy.on_shutdown(回调函数名字)

2.日志函数

rospy.logdebug("++")  ---- 不会输出 

rospy.loginfo("++")  ---- 默认白色字体

rospy.logwarn("++")  ---- 默认黄色字体

rospy.logerr("++")  ---- 默认红色字体

rospy.logfatal("++")  ---- 默认红色字体

二、ROS中的头文件和源文件

2.1 本节内容介绍

本节主要介绍ROS的C++实现中,如何使用头文件和源文件方式封装代码,具体内容如下:

1.设置头文件,可执行文件作为源文件

2.分别设置头文件,源文件和可执行文件

在ROS中关于头文件的使用,核心内容在于CMakeList.txt文件的篇日志,不同的封装方式,配置上也有差异

2.2 自定义头文件的调用

2.2.1 需求及流程

Ⅰ.需求:设计头文件,可执行文件本身作为源文件

Ⅱ.流程:编写头文件;编写可执行文件;编辑配置文件并执行 

2.2.2 自定义头文件的调用

完整流程:

Ⅰ.创建功能包:plumbing_head ,依赖于roscpp rospy std_msgs

Ⅱ.创建头文件:在功能包下的include 下有个和功能包重名的目录,建立头文件hello.h

Ⅲ.声明头文件保护及头文件实现

#ifndef _HELLO_H
#define _HELLO_H
/*
声明namespace ,namespace下面声明一个类,类下声明一个函数run
*/
namespace hello_ns
{class Myhello{public:void run();};
}#endif

Ⅳ.可执行文件实现

①配置includePath路径,在c_cpp_properties.json文件中配置头文件的路径

/home/liuhongwei/demo03_ws/src/plumbing_head/include

加入includePath中,加入结果如下

      "includePath": ["/opt/ros/melodic/include/**","/home/liuhongwei/demo01_ws/src/helloworld1/include/**","/usr/include/**","/home/liuhongwei/demo03_ws/devel/include/**","/home/liuhongwei/demo03_ws/src/plumbing_head/include/**"],

②源文件实现

#include "ros/ros.h"
#include "plumbing_head/hello.h"namespace hello_ns
{void Myhello::run(){ROS_INFO("run 函数执行...............");}
}int main(int argc, char  *argv[])
{setlocale(LC_ALL,"");ros::init(argc,argv,"hello_head");hello_ns::Myhello mehello;mehello.run();return 0;
}

③配置 CMakeList.txt  

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(include${catkin_INCLUDE_DIRS}
)

④编译执行

2.3 自定义源文件的调用 

2.3.1 需求及流程

Ⅰ.需求:设计头文件和源文件,在可执行文件中包含头文件

Ⅱ.流程:编写头文件;编写源文件;编写可执行文件;配置配置文件并执行

 2.3.2 C++自定义源文件的使用

Ⅰ.创建功能包 plumbing_head_src,依赖为roscpp rospy std_msgs

Ⅱ.创建头文件 hello.h

#ifndef _HELLO_H
#define _HELLO_Hnamespace hello_ns
{class Myhello{public:void run();};
}#endif

Ⅲ.创建源文件 hello.cpp

#include "plumbing_head_src/hello.h"
#include "ros/ros.h"
namespace hello_ns
{void Myhello::run(){ROS_INFO("源文件中的run函数................");}
}

Ⅳ.修改c_cpp_properties.json下面的includPath文件(同上)

Ⅳ.创建可执行文件 use_hello.cpp

#include "ros/ros.h"
#include "plumbing_head_src/hello.h"int main(int argc, char  *argv[])
{setlocale(LC_ALL,"");ros::init(argc,argv,"hello_src");hello_ns::Myhello my;my.run();return 0;
}

Ⅴ.配置Cmakelist&头文件的配置

①修改 include_directories

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(include${catkin_INCLUDE_DIRS}
)

②配置 C++库  头文件;源文件位置

## Declare a C++ libraryadd_library(head_srcinclude/plumbing_head_src/hello.hsrc/hello.cpp)

③修改两处

 add_dependencies(head_src ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})target_link_libraries(head_src${catkin_LIBRARIES})

Ⅴ.配置Cmakelist&可执行文件的配置

①修改add_executable

 add_executable(use_hello src/use_hello.cpp)

②修改 add_dependencies

 add_dependencies(use_hello ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

③修改 target_link_libraries

由于use_hello要依赖于head_src 做如下修改

 target_link_libraries(use_hellohead_src${catkin_LIBRARIES})

Ⅵ.编译执行

 2.3.3 python自定义源文件的使用 

1.需求:

首先建立一个python文件A,再创建python文件 useA ,在useA中导入A并调用A的实现

2.rosrun的路径是什么?

[INFO] [1649919184.762048]: 执行时参考路径:/home/liuhongwei/demo03_ws

path = os.path.abspath(".")
rospy.loginfo("执行时参考路径:%s",path)

3.由上述基础后

Ⅰ.我们在同一scripts目录下建立两文件,demo01.py 和 tools.py

#! /usr/bin/env python
#encoding:utf-8#1.导包 
import rospy
from std_msgs.msg import String
import os
#import tools
if __name__ == "__main__":#2.初始化 ROS 节点:命名(唯一)rospy.init_node("lhw")#3.实例化 发布者 对象pub = rospy.Publisher("car",String,queue_size=10)#4.组织被发布的数据,并编写逻辑发布数据msg = String()  #创建 msg 对象msg_front = "hello 你好"count = 0  #计数器 # 设置循环频率rate = rospy.Rate(1)while not rospy.is_shutdown():#拼接字符串msg.data = msg_front + str(count)pub.publish(msg)rate.sleep()rospy.loginfo("写出的数据:%s",msg.data)count += 1
#! /usr/bin/env python
#coding=utf-8lhwnb = 100

Ⅱ.机器在处理好配置文件及赋予可执行权限后,import tools会抛出异常,

No Module named "tools"

异常原因是找不到tools模块?为什么呢?因为环境变量出问题了,python执行过程中,如果导入了某个模块,会在当前目录下看是否包含,rosrun的当前目录不是scripts目录,因此找不到。

Ⅲ.如何解决①:可以声明python的环境变量,当依赖某个模块时,先去指定的环境变量中的环境中查找依赖(不推荐)

import sys
#import tools
if __name__ == "__main__":#设置临时环境变量sys.path.insert(0,"/home/liuhongwei/demo03_ws/src/plumbing_pub_sub/scripts")

Ⅳ.如何解决②

动态获取路径:当前工作空间的路径 + 内部路径

①当前工作空间的绝对路径:rosrun的路径 即到..../demo03_ws/的路径,可以用os.path.absolute获取

②内部路径:鼠标右击获取scripts下的路  src/plumbing_pub_sub/scripts

    path = os.path.abspath(".")sys.path.insert(0, path + "src/plumbing_pub_sub/scripts")

③完整代码实现

#! /usr/bin/env python
#encoding:utf-8#1.导包 
import rospy
from std_msgs.msg import String
import os
import sys
#import tools
if __name__ == "__main__":path = os.path.abspath(".")sys.path.insert(0, path + "src/plumbing_pub_sub/scripts")rospy.init_node("lhw")#3.实例化 发布者 对象pub = rospy.Publisher("car",String,queue_size=10)#4.组织被发布的数据,并编写逻辑发布数据msg = String()  #创建 msg 对象msg_front = "hello 你好"count = 0  #计数器 # 设置循环频率rate = rospy.Rate(1)while not rospy.is_shutdown():#拼接字符串msg.data = msg_front + str(count)pub.publish(msg)rate.sleep()rospy.loginfo("写出的数据:%s",msg.data)count += 1

这篇关于Chapter3 ROS通信机制----进阶篇(Ⅰ)ROS常用函数及自定义头文件源文件的调用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/395606

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

常用的jdk下载地址

jdk下载地址 安装方式可以看之前的博客: mac安装jdk oracle 版本:https://www.oracle.com/java/technologies/downloads/ Eclipse Temurin版本:https://adoptium.net/zh-CN/temurin/releases/ 阿里版本: github:https://github.com/

30常用 Maven 命令

Maven 是一个强大的项目管理和构建工具,它广泛用于 Java 项目的依赖管理、构建流程和插件集成。Maven 的命令行工具提供了大量的命令来帮助开发人员管理项目的生命周期、依赖和插件。以下是 常用 Maven 命令的使用场景及其详细解释。 1. mvn clean 使用场景:清理项目的生成目录,通常用于删除项目中自动生成的文件(如 target/ 目录)。共性规律:清理操作

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL