# 创建工作空间同时进入要创建包的位置
cd code/
mkdir -p My_Robot/src
cd My_Robot/src
接下来执行
ros2 pkg create chassis_control --build-type ament_cmake --dependencies rclcpp
创建自己第一个底盘控制包
就会在src下生成功能包
ros2 pkg create village_li --build-type ament_python --dependencies rclpy
输入命令
colcon build
即使chassis_control包里面没有写文件,也会进行编译
编译完成后
My_Robot文件夹下将会多出来build install log 三个文件夹和 src存放功能包的同级目录
+------+-----+----------+--------+---+ OPI5 +---+--------+----------+-----+------+
| GPIO | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | GPIO |
+------+-----+----------+--------+---+----++----+---+--------+----------+-----+------+
| | | 3.3V | | | 1 || 2 | | | 5V | | |
| 47 | 0 | SDA.5 | IN | 1 | 3 || 4 | | | 5V | | |
| 46 | 1 | SCL.5 | IN | 1 | 5 || 6 | | | GND | | |
| 54 | 2 | PWM15 | IN | 1 | 7 || 8 | 0 | IN | RXD.0 | 3 | 131 |
| | | GND | | | 9 || 10 | 0 | IN | TXD.0 | 4 | 132 |
| 138 | 5 | CAN1_RX | IN | 0 | 11 || 12 | 1 | IN | CAN2_TX | 6 | 29 |
| 139 | 7 | CAN1_TX | IN | 1 | 13 || 14 | | | GND | | |
| 28 | 8 | CAN2_RX | IN | 1 | 15 || 16 | 1 | IN | SDA.1 | 9 | 59 |
| | | 3.3V | | | 17 || 18 | 1 | IN | SCL.1 | 10 | 58 |
| 49 | 11 | SPI4_TXD | IN | 1 | 19 || 20 | | | GND | | |
| 48 | 12 | SPI4_RXD | IN | 1 | 21 || 22 | 1 | IN | GPIO2_D4 | 13 | 92 |
| 50 | 14 | SPI4_CLK | IN | 1 | 23 || 24 | 1 | IN | SPI4_CS1 | 15 | 52 |
| | | GND | | | 25 || 26 | 1 | IN | PWM1 | 16 | 35 |
+------+-----+----------+--------+---+----++----+---+--------+----------+-----+------+
| GPIO | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | GPIO |
+------+-----+----------+--------+---+ OPI5 +---+--------+----------+-----+------+
服务流程如下
------------- 服务端 <----------
| |
| |
发送响应-借钱结果 发送请求-欠条
| |
| |
----------> 客户端 ------------
服务注意事项
使用服务时的一些可用命令便于方便查看调试
int64 a
int64 b
---
int64 num
上方的是客户端发送请求的数据结构定义
下方的是服务端响应结果的数据结构定义
编写自定义服务接口如下
waiting for service to become available...
requester: making request: village_interfaces.srv.BorrowMoney_Request(name='fish', money=5)
response:
village_interfaces.srv.BorrowMoney_Response(success=True, money=5)
ros2 param list # 查看目前节点的所有参数
ros2 param describe /turtlesim background_b # 查看某节点下的一个参数的所有描述
ros2 param get /turtlesim background_b # 获取某节点下的一个参数的当前值
ros2 param set /turtlesim background_g 156 # 设置某节点的一个参数的值
# 调用该命令时只是临时修改
ros2@ubuntu: ros2 param dump /turtlesim # 保存某节点的参数值(拍照参数截屏)
Saving to: ./turtlesim.yaml
使用 cat ./turtlesim.yaml 的命令查看参数
重新启动ROS节点还是会变成默认值,但此时我们有了yaml文件,可以快速读取参数
使用
ros2 param load /turtlesim ./turtlesim.yaml
命令快速加载存在yaml里的参数
当然以上还是会慢一步,先开始默认节点,在使用ros param load的命令加载参数
有没有什么方法可以一开节点就自动加载列表呢,有,很简单
ros2 run turtlesim turtlesim_node --ros-args --params-file ./turtlesim.yaml
使用该命令在开启节点的时候就把参数yaml加载进去
ros2 topic hz /my_first_PUB # 查看该话题的发布速度
li4.py文件当中使用
# 声明 1、声明参数
self.declare_parameter("writer_timer_period", 5)
# 使用获取 2、时间周期中获取发布小说速度的参数
timer_period = self.get_parameter("writer_timer_period").get_parameter_value().integer_value
self.timer.timer_period_ns = timer_period * (1000 * 1000 * 1000)
// wang2.cpp 文件使用
// 书本的单价 参数类型
unsigned int novel_price = 1; // 目前书本的单价为1
// 声明参数
this->declare_parameter<std::int64_t>("novel_price", novel_price);
// 获取书本最新的单价
this->get_parameter("novel_price", novel_price);
流程是张三将钱通过服务给王二,然后王二凑够对应章节数量的小说返回给张三,这个过程看似没有问题,假设你是张三,你就会发现下面这些问题:
如果这些问题体现在机器人上,可能是这样子的。我们通过服务服务发送一个目标点给机器人,让机器人移动到该点:
上面的场景在机器人控制当中经常出现,比如控制导航程序,控制机械臂运动,控制小乌龟旋转等,很显然单个话题和服务不能满足我们的使用,因此ROS2针对控制这一场景,基于原有的话题和服务,设计了动作(Action)这一通信方式来解决这一问题。
ros2 action list # 查看当前获取系统中的action列表
ros2 action list -t # 查看所有action列表 同时查看类型
ros2 interface show xxx # 查看该类型的动作下的自定义服务接口是什么数据类型
ros2 action info xxx # 通过action的名字来查看这个action的客户端和服务端的数量以及名字
ros2 action send_goal /turtle/rotate_absolute turtlesim/action/RotateAbsolute "{theta: 0}" # 发送action请求到服务端,这里演示的是发送小乌龟转动的绝对角度给服务端
ros2 action send_goal /turtle/rotate_absolute turtlesim/action/RotateAbsolute "{theta: 0}" --feedback # 通过使用feedback将每一个时刻内小乌龟的弧度进行命令行打印
# 导入库
# 1.导入头文件
from launch import LaunchDescription
from launch_ros.actions import Node
# 2、定义
# 定义函数名称为:generate_launch_description
def generate_launch_description():
# 3、创建节点描述
# 创建Actions.Node对象li_node,标明李四所在位置
li4_node = Node(
package="village_li",
executable="li4_node"
)
# 创建Actions.Node对象wang2_node,标明王二所在位置
wang2_node = Node(
package="village_wang",
executable="wang2_node"
)
# 4、搭建描述launch
# 创建LaunchDescription对象launch_description,用于描述launch文件
launch_description = LaunchDescription([li4_node, wang2_node])
# 返回让ROS2根据launch描述执行节点
return launch_description
pass
from setuptools import setup
# launch文件添加
from glob import glob
import os
package_name = 'village_li'
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
(os.path.join('share', package_name, 'launch'),glob('launch/*.launch.py')) # 将launch文件下的所有launch复制到install/功能包/share下
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='orangepi',
maintainer_email='[email protected]',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
"li4_node=village_li.li4:main", # 重点新添加 搭配 source install/setup.bash 使用 这样子系统才能找到village_li这个包
"li3_node=village_li.li3:main"
],
},
)
# 将launch文件添加到share下的功能包目录当中
install(DIRECTORY launch
DESTINATION share/${PROJECT_NAME}
)
# 导入库
from launch import LaunchDescription
from launch_ros.actions import Node
# 定义函数名称为:generate_launch_description
def generate_launch_description():
# 创建Actions.Node对象li_node,标明李四所在位置
li4_node = Node(
package="village_li",
executable="li4_node",
output='screen', #四个可选项
parameters=[{'writer_timer_period': 1}]
)
# 创建Actions.Node对象wang2_node,标明王二所在位置
wang2_node = Node(
package="village_wang",
executable="wang2_node",
parameters=[{'novel_price': 2}]
)
# 创建另外一个命名空间下的,创建Actions.Node对象li_node,标明李四所在位置
li4_node2 = Node(
package="village_li",
namespace="mirror_town",
executable="li4_node",
parameters=[{'writer_timer_period': 2}]
)
# 创建另外一个命名空间下的,Actions.Node对象wang2_node,标明王二所在位置
wang2_node2 = Node(
package="village_wang",
namespace="mirror_town",
executable="wang2_node",
parameters=[{'novel_price': 1}]
)
# 创建LaunchDescription对象launch_description,用于描述launch文件
launch_description = LaunchDescription([li4_node,wang2_node,wang2_node2,li4_node2])
# 返回让ROS2根据launch描述执行节点
return launch_description