ROS2学习记录——节点

创建功能包

创建Python功能包

# 创建Python功能包 
$ ros2 pkg create --build-type ament_python --license Apache-2.0 {PKG_NAME}

创建好功能包之后,生成的目录结构如下图所示。PKG_NAME/PKG_NAME下为主要开发区域,例如编写了一个python_node.py的程序。

编写好程序后在setup.py中进行配置,对需要构建生成的可执行文件进行声明。

    entry_points={
        'console_scripts': [
            'python_node = demo_python_pkg.python_node:main'  #exe_name = PKG_NAME.file_name:func_name 
        ],
    },

package.xml中添加依赖信息,虽然不会影响构建过程,但有助于迅速阅读依赖信息。

<depend>rclpy</depend>

进行构建。

#构建
colcon build

构建完成后,生成相应的功能包,存放在install/目录下。
在这里插入图片描述

在执行可执行文件之前,需要更改环境变量,setup.bash提供了修改环境变量的脚本,需要注意的是,该脚本需要在一级目录下执行,且修改的环境变量仅在当前终端有效。

# 更改环境变量
$ source install/setup.bash

在修改完环境变量后,就可以运行可执行文件了。

# 运行可执行文件
$ ros2 run demo_python_pkg python_node

创建C++功能包

# 创建C++功能包 
$ ros2 pkg create --build-type ament_cmake --license Apache-2.0 {PKG_NAME}

创建功能包后,功能包目录如下图,其中src/目录存放.cpp文件,include/目录存放相应的头文件。
在这里插入图片描述
配置CMakeLists

find_package(rclcpp REQUIRED) # 查找对应的头文件和库文件
add_executable(cpp_node src/cpp_node.cpp)  # 添加可执行文件cpp_node, 编译路径为src/cpp_node.cpp, 这是相对于CMakeLists的路径
#target_include_directories(cpp_node PUBLIC ${rclcpp_INCLUDE_DIRS})
#target_link_libraries(cpp_node ${rclcpp_LIBRARIES})
ament_target_dependencies(cpp_node rclcpp)  #进行包含和链接,替代上面两句语句

install(TARGETS cpp_node
  DESTINATION lib/${PROJECT_NAME}
)#将生成的可执行文件cpp_node拷贝至lib/${PROJECT_NAME}下,否则在build目录下

package.xml中添加依赖信息,虽然不会影响构建过程,但有助于迅速阅读依赖信息。

<depend>rclcpp</depend>

进行构建,注意,在一级目录终端进行编译,输入以下指令会编译(构建)当前目录下的所有功能包。

$ colcon build

更改环境变量。

$ source install/setup.bash

运行可执行文件。

# 运行可执行文件
$ ros2 run demo_cpp_pkg cpp_node

Workspace—多功能包的最佳实践

创建workspace,命名可自定义。

$ mkdir -p workspace/src
$ mv demo_cpp_pkg workspace/src/
$ mv demo_python_pkg workspacce/src/
$ rm -rf build/ install/ log/

workspace中打开集成终端,就可以进行构建了。生成的文件目录在workspace/下.
在这里插入图片描述

进行选择性构建。

$ colcon build --packages-select {PKG_name}

若功能包的构建存在依赖关系,可以将被依赖的功能包添加至对应功能包的package.xml里。例如,若demo_python_pkg的构建依赖于demo_cpp_pkg功能包的构建,那么就可以在demo_python_pkg功能包的package.xml中添加依赖。在进行构建后,会优先构建被依赖的功能包。

<depend>demo_cpp_pkg</depend>

创建自定义Node

在ROS2中,提供了自带的Node类,那么就可以通过继承,创建属于自己的Node类。

Python创建

class MyNode(Node):
    def __init__(self, node_name_val:str, name_val:str, age_val:int)->None:
        super().__init__(node_name_val) # 调用父类__init__
        self.name = name_val
        self.age = age_val

    def eat(self, food:str):
        self.get_logger().warn(f"{self.name}喜欢吃{food}")
# 使用自己的创建的类,就可以调用自己定义的属性、方法,以及Node类的属性和反法
rclpy.init()
node = MyNode('my_node', '王五', 23)
node.get_logger().warn(f"{node.name},年龄{node.age}岁")
node.eat('茅台')
rclpy.spin(node)
rclpy.shutdown()
[WARN] [1732333154.004132284] [my_node]: 王五,年龄23岁
[WARN] [1732333154.004452767] [my_node]: 王五喜欢吃茅台

C++创建

class MyNode : public rclcpp::Node
{
private:
    std::string name_;
    int age_;
public:
    MyNode(const std::string &node_name, const std::string name, const int age)
    :Node(node_name) { //调用基类构造函数
        this->name_ = name;
        this->age_ = age;
    };
    void eat(const std::string food_name) {
        RCLCPP_INFO(this->get_logger(), "我叫%s, 今年%d岁, 喜欢吃%s", this->name_.c_str(), this->age_, food_name.c_str());
    };
};

ROS2中常用语法

C++共享指针

#include <iostream>
#include <memory>

class Ball
{
public:
    Ball() {
        std::cout << "The ball appears" << std::endl;
    }
    ~Ball() {
        std::cout << "The ball disappears" << std::endl;
    }
    void Bounce() {
        std::cout << "The ball jumps" << std::endl;
    }
};

int main() {
    std::cout << "Make a shared ptr!" << std::endl;
    // 使用 make_shared 会动态分配一块内存, 创建对应的资源,
    std::shared_ptr<Ball> sp1 = std::make_shared<Ball>();
    std::cout << "sp1.use_count = " << sp1.use_count() << std::endl;
    auto sp2 = sp1; //增加引用
    std::cout << "sp1.use_count = " << sp1.use_count()
        << ", sp2.use_count = " << sp2.use_count() << std::endl;
    std::cout << "Reset the sp1!" << std::endl;
    sp1.reset();
    std::cout << "Reset the sp2!" << std::endl;
    sp2.reset(); //当引用计数为0时,自动释放内存
    return 0;
}
#输出
Make a shared ptr!
The ball appears
sp1.use_count = 1
sp1.use_count = 2, sp2.use_count = 2
Reset the sp1!
Reset the sp2!
The ball disappears

共享指针引入了引用计数的机制,当所有引用被reset后,会自动释放其指向的内存,降低手动释放内存带来的负担。共享指针主要语法:

//共享指针的创建
std::shared_ptr<Type> sp1 = std::make_shared<Type>(Initial_Val);
//共享指针的重置
sp.reset();
//获取原始指针
p = sp.get();

C++ Lambda函数

#include <iostream>
#include <algorithm>

/* Lamda函数 */
int main() {
    auto add = [](int a, int b) -> int {
        return a+b;
    };
    int sum = add(10, 20);
    auto print_sum =[sum]() -> void {
        std::cout << "sum = " << sum << std::endl;
    }; 
    print_sum();
    return 0;
}
auto 函数名= [捕获列表](形参列表) -> 返回值类型 {
	...
}

函数包装器

#include <iostream>
#include <functional>

/* 自由函数 */
void free_func(const std::string func_name) {
    std::cout << func_name << ":the free function is called!" << std::endl;
}

/* 成员函数 */
class Fun
{
public:
    void member_func(const std::string func_name) {
        std::cout << func_name << ":the member function is called!" << std::endl;
    }
};

/* Lambda函数 */
auto lamda_func = [](const std::string func_name) -> void {
    std::cout << func_name << ":the lamda function is called!" << std::endl;
};

int main() {
    Fun my_func;
    std::function<void(const std::string)> func1 = free_func;
    std::function<void(const std::string)> func2 = lamda_func;
    std::function<void(const std::string)> func3 = std::bind(&Fun::member_func, &my_func, std::placeholders::_1);
    func1("Free_FUN");
    func2("LAMDA_FUN");
    func3("MRM_FUN");
    return 0;
}

注意常见三种函数的包装格式。

C++多线程与回调函数

声明:以上内容均学习于“鱼香ROS”博主系列课程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值