为什么做该博客?该博客的特点是什么?
随着DeepSeek、ChatGPT等AI技术的崛起,促使机器人技术发展到了新的高度,诞生了宇树科技、特斯拉为代表的人形机器人,四足机器人等等,越来越多的科技巨头涌入机器人赛道,行业对于相关人才的需求也随之达到了顶峰。本博客的内容是替你阅读所有关于机器人的经典书籍,采用书籍瘦身计划,帮你提炼出核心内容,采用最通俗易懂的语言来解释原理,将书读薄。大大缩短学习时间,助你快速成为机器人时代的佼佼者。
《ROS2机器人开发 从入门到实践》是一本从零基础到实战落地的机器人开发指南,通过"基础理论+工具详解+项目实战"的三阶学习路径,带你掌握机器人操作系统ROS2核心架构。书中不仅包含通信机制、URDF建模等必备知识,更有SLAM导航、机械臂控制等12个工业级案例,配合Git代码库和仿真环境配置指南,帮助开发者快速实现从算法仿真到真机部署的完整闭环。无论是学生、工程师还是科研人员,都能通过本书构建系统的ROS2开发能力,抢占机器人技术前沿阵地。
1、先在系统中安装Agent,在主目录下新建chapt9/fishbot_ws/src目录,接着克隆micro-ROS Agert源码到src目录,指令如下:
cd fishbot_ws/src
git clone https://github.com/micro-ROS/micro-ROS-Agent.git -b $ROS_DISTRO
git clone https://github.com/micro-ROS/micro_ros_msgs.git -b $ROS_DISTRO
2、进行功能包构建,之后就可以直接运行Agent了
colcon build
source install/setup.bash
ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888
启动了节点,并使用udp4作为传输协议,指定端口号为8888
3、在原来的工程里引入micro-ROS,修改原工程platform.ini文件,添加依赖库和配置
board_microros_transport = wifi
lib_deps =
https://gitee.com/ohhuo/micro_ros_platformio.git
这里添加了micro-ROS的platformio版本依赖库,并添加了传输协议配置项,指定使用wifi与ROS2 Agent进行连接
4、修改main.cpp代码,来创建第一个micro-ROS节点
#include
#include
#include
#include
#include
float out_right_speed;
//声明相关的结构体对象
rcl_allocator t allocator;
rclc_support_t support;
rclc_executor_t executor;
rcl_node_t node;
//单独创建一个任务运行micro-ROS,相当于一个线程
void micro_ros_task(void *parameter){
//1.设置传输协议并延时等待设置完成
IPAddress agent_ip;
agent_ip.fromstring("192.168.4.136");//替换为你自己主机的IP地址
set_microros_wifi_transports("WIPI_NAME", "WIFI_PASSSWORD", agent_ip,8888)
delay(2000);
//2,初始化内存分配器
allocator = rcl get_default_allocator();
//3.初始化support
rclc_support_init(&support,0,NULL,&allocator)
//4.初始化节点fishbot_motion_control
rclc_node_init_default(&node, "fishbot_motion_contrrol","",&support);
//5.初始化执行器
unsigned int num handles = 0;
rclc_executor_init(&executor, &support.context, num_handlLes,&allocator);
//循环执行器
rclc_executor_spin(&executor);
}
void setup(){
//创建任务运行microrostask
xTaskCreate(micro_ros_task,
"micro ros",
10240,
NULL
1,
NULL
);
}
5、将代码下载到开发板,连接成功后可以看到micro-ROS Agent终端的提示,表示连接成功
在终端运行agent
ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888
6、打开新的终端查看节点列表,可以看到来自微控制器fishbot_motion_control节点
ros2 node list
1、在main.cpp添加如下代码
#include
rcl subscription t subscriber;//订阅者
geometry_msgs_msg_Twist sub_msg;//存储订阅到的速度消息
void twist_callback(const void *msg_in){
//将接收到的消息指针转化为geometry_msgs_msg_Twist类型
const geometry_msgs_msg_Twist *twist_msg = (const geometry_msgs_msg_Twist *)msg_in;
//运动学逆解并设置速度
kinematics.kinematic_inverse (twist_msg->linear.x * 1000, twist_msg->angular.z, out_left_speed, out_right_speed);
pid_controller[0].update_target(out_left_speed);
pid_controller[1].update_target (out_right_speed);
}
void micro_ros_task(void *parameter){
//4.初始化执行器
unsigned int num_handles = 0+1;
rclc_executor_init(&executor, &support.conntext, num_handles, &allocator);
//5.初始化订阅者并添加到执行器中
rclc_subscription init_best_effort(&subscriber, &node,ROSIDL_GET_MSG_TYPE_SUPPORT(geometry_msgs, msg, Twist),"/cmd_vel");
rclc_executor_add_subscription(&executor, &subsscriber, &sub_msg , &twist_callback, ON_NEW_DATA);
rclc_executor_spin(&executor);
}
2、将代码下载到开发板,查看话题,可以看到多出了一个/cmd_vel话题的订阅者,消息接口类型是geometry_msgs/msg/Twist,可以使用ROS2的键盘控制节点来向这个话题发布速度指令
ros2 run telop_twist_keyboard telop_twist_keyboard
1、修改main.cpp,添加发布者、进行时间同步和创建定时器
#include
#include
rcl_publisher_t odom_publisher;//发布者
nav_msgs_msg_Odometry odom_msg;//里程计消息
rcl_timer_t timer;//定时器,可以定时调用某个函数
//在定时器回调函数中完成话题发布
void callback_publisher(rcl_timer_t *timer, int64_t last_call_time){
odom t odom=kinematics.get_odom();//获取里程计
int64_tstamp=rmw_uros_epoch_millis();//获取当前时间
odom_msg.header.stamp.sec = static_cast<int32_t>(stamp/1000);//秒部分
//纳秒部分
odom_msg.header.stamp.nanosec = static_cast<uint322_t>((stamp ¥1000) * 1e6)
odom_msg.pose.pose.position.x = odom.x;
odom_msg.pose.pose.position.y = odom.y;
odom_msg.pose.pose.orientation.w = cos (odom.angle * 0.5);
odom_msg.pose.pose.orientation.x = 0;
odom_msg.pose.pose.orientation.y = 0;
odom_msg.pose.pose.orientation.z = sin(odom.angle*0.5)
odom_msg.twist.twist.angular.z = odom.angle_speed
odom_msg.twist.twist.linear.x = odom.linear_speed
//发布里程计
if(rcl_publish (&odom_publisher, &odom_msg, NULL)!==RCL_RET_OK){
Serial.printf("error: odom publisher failed!\n");
}
}
void micro_ros_task(void *parameter){
unsigned int num_handles = 0 + 2;
rclc_executor_init(&executor,&support.conteext, num_handles, &allocator);
//6.初始化发布者和定时器
odom_msg.header.frame_id = micro_ros_string_utilities_set(odom_msg.header.frame_id, "odom");
odom_msg.child_frame_id = micro_ros_string_utilities_set(odom_msg.child_frrame_id, "base_footprint");
rclc_publisher_init_best_effort(&odom_publisher, &node,ROSIDL_GET_MSG_TYPE_SUPPORT(nav_msgs, msg, odomnetry),"/odom");
//7.时间同步
while(!rmw_uros_epoch_synchronized()){//如果没有同步
rmw_uros_sync_session(1000);//尝试进行时间同步
delay (10);
}
//8.创建定时器,间隔50ms发布调用一次callback_publisher发布里程计话题
rclc_timer_init_default(&timer, &support,RCL_MSS_TO_NS (50), callback_publisher);
rclc_executor_add_timer(&executor,&timer);
//循环执行器
rclc_executor_spin(&executor);
}
2、重新构建工程并下载到开发板,在主机端运行Agent,连接成功后可以看到里程计话题已经出现了
完成里程计话题发布和速度命令的控制就完成了机器人底盘控制系统了,下面可以实现机器人建图和导航
1、下载雷达串口转wifi转接板的驱动
cd chapt9/fishbot_ws/src
git clone https://github.com/fishros/ros_serial2wifi.git
构建工作空间,运行串口转wifi驱动,确保8889端口映射到本地的/tmp/tty_laser
colcon build
source install/setup.bash
ros2 run ros_serial2wifi tcp_server --ros-args -p serial_port:=/tmp/tty_laser
之后使用cat /tmp/tty_laser可以查看到来自雷达的数据,但是需要雷达驱动才能解析出数据的内容
2、下载雷达驱动
cd fishbot_ws/src
git clone https://github.com/fishros/ydlidar_ros2.git -b fishbot
修改雷达驱动的配置文件ydlidar_ros2/params/ydlidar.yaml,修改端口号为/tmp/tty_laser,修改frame_id为laser_link,重新构建工作空间并运动雷达驱动
colcon build
source install/setup.bash
ros2 launch ydlidar ydlidar_launch.py
接着就可以看到雷达话题/scan了,通过命令行就可以输出一帧话题数据
ros2 topic echo /scan --once
至此雷达的话题有了,目前需要建图和导航,还需要准备坐标变换
1、在chapt9/fishbot_ws/src下新建fishbot_description功能包,采用默认的构建类型,在src/fishbot_description下新建urdf目录,新建fishbot.urdf文件,并编写如下代码
ros2 pkg create fishbot_description --build-type ament_cmake --license Apache-2.0
<?xml version="1.0"?>
<robot name="fishbot">
<link name="base_footprint" />
<!-- base link -->
<link name="base_link">
<visual>
<origin xyz="0 0 0.0" rpy="0 0 0" />
<geometry>
<cylinder length="0.12" radius="0.10" />
</geometry>
<material name="blue">
<color rgba="0.1 0.1 1.0 0.5" />
</material>
</visual>
</link>
<joint name="base_joint" type="fixed">
<parent link="base_footprint" />
<child link="base_link" />
<origin xyz="0.0 0.0 0.076" rpy="0 0 0" />
</joint>
<!-- laser link -->
<link name="laser_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<cylinder length="0.02" radius="0.02" />
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 0.5" />
</material>
</visual>
</link>
<joint name="laser_joint" type="fixed">
<parent link="base_link" />
<child link="laser_link" />
<origin xyz="0 0 0.075" rpy="0 0 0" />
</joint>
</robot>
2、在CMakeLists.txt中添加复制urdf目录到install下的指令
install(DIRECTORY urdf DESTINATION share/${PROJECT_NAME})
3、接着新建fishbot_bringup功能包,采用默认构建类型,将加载URDF和启动相关的命令放到该功能包中,在src/fishbot_bringup下新建launch目录,接着新建urdf2tf.launch.py,编写如下内容:
ros2 pkg create fishbot_bringup --build-type ament_cmake --license Apache-2.0
import launch
import launch_ros
from ament_index_python.packages import get_package_share_directory
def generate_launch_description():
#获取默认路径
urdf_tutorial_path = get_package_share_directory('fishbot_description')
fishbot_model_path = urdf_tutorial_path + '/urdf/fishbot.urdf'
#为launch声明参数
action_declare_arg_mode_path = launch.actions.DeclareLaunchArgument(name='model',default_value=str(fishbot_model_path),description='URDF的绝对路径')
#获取文件内容生成新的参数
robot_description = launch_ros.parameter_descriptions.ParameterValue(launch.substitutions.Command(['cat ', launch.substitutions.LaunchConfiguration('model')]),value_type=str)
#状态发布节点
robot_state_publisher_node = launch_ros.actions.Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[{'robot_description': robot_description}]
)
#关节状态发布节点
joint_state_publisher_node = launch_ros.actions.Node(
package='joint_state_publisher',
executable='joint_state_publisher',
)
return launch.LaunchDescription([
action_declare_arg_mode_path, joint_state_publisher_node,
robot_state_publisher_node,
])
4、在CMakeLists.txt中添加复制launch目录到install下的指令
install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
5、重新构建工程,启动launch文件
colcon build
source install/setup.bash
ros2 launch fishbot_bringup urdf2tf.launch.py
1、在fishbot_ws/src/fishbot_bringup/src下新建odom2tf.cpp,编写如下代码
#include
#include
#include
#include
#include
class OdomTopic2TF : public rclcpp::Node
{
public:
OdomTopic2TF(std::string name) : Node(name)
{
odom_subscribe_ = this->create_subscription<nav_msgs::msg::Odometry>(
"odom", rclcpp::SensorDataQoS(),
std::bind(&OdomTopic2TF::odom_callback_, this, std::placeholders::_1));
//创建一个tf2_ros::TransformBroadcaster用于广播坐标变换
tf_broadcaster_ = std::make_unique<tf2_ros::TransformBroadcaster>(this);
}
private:
rclcpp::Subscription<nav_msgs::msg::Odometry>::SharedPtr odom_subscribe_;
std::unique_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster_ ;
//回调函数,处理接收到的odom消息,并发布tf
void odom_callback_(const nav_msgs::msg::Odometry::SharedPtr msg){
geometry_msgs::msg::TransformStamped transform;
transform.header=msg->header;//使用消息的时间戳利和框架ID
transform.child_frame_id = msg->child_frame_id;
transform.transform.translation.x = msg->pose.pose.position.x;
transform.transform.translation.y = msg->pose.pose.position.y;
transform.transform.translation.z = msg->pose.pose.position.z;
transform.transform.rotation.x = msg->pose.pose.orientation.x;
transform.transform.rotation.y = msg->pose.pose.orientation.y;
transform.transform.rotation.z = msg->pose.pose.orientation.z;
transform.transform.rotation.w = msg->pose.pose.orientation.w;
//广播坐标变换信息
tf_broadcaster_->sendTransform(transform);
};
};
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<OdomTopic2TF>("odom2tf");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
2、修改CMakeLists.txt,注册odom2tf节点,添加如下代码:
find_package(rclcpp REQUIRED)
find_package(tf2 REQUIRED)
find_package(tf2_ros REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(nav_msgs REQUIRED)
add_executable(odom2tf src/odom2tf.cpp)
ament_target_dependencies(odom2tf rclcpp tf2 nav_msgs geometry_msgs tf2_ros)
install(TARGETS odom2tf DESTINATION lib/${PROJECT_NAME})
3、重新构建功能包
colcon build
source install/setup.bash
先运行micro_ros_agent让机器人接入,确保odom话题数据正常
ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888
然后运行odom2tf节点
ros2 run fishbot_bringup odom2tf
1、将启动底盘和雷达的指令都放到一个launch里面,在fishbot_ws/src/fishbot_bringup/launch下新建bringup.launch.py,编写如下代码:
import launch
import launch_ros
from ament_index_python.packages import get_package_share_directory
from launch.launch_description_sources import PythonLaunchDescriptionSource
def generate_launch_description():
fishbot_bringup_dir = get_package_share_directory('fishbot_bringup')
ydlidar_ros2_dir = get_package_share_directory('ydlidar')
urdf2tf = launch.actions.IncludeLaunchDescription(
PythonLaunchDescriptionSource(
[fishbot_bringup_dir, '/launch', '/urdf2tf.launch.py']),
)
odom2tf = launch_ros.actions.Node(
package='fishbot_bringup',
executable='odom2tf',
output='screen'
)
microros_agent = launch_ros.actions.Node(
package='micro_ros_agent',
executable='micro_ros_agent',
arguments=['udp4', '--port', '8888'],
output='screen'
)
ros_serial2wifi = launch_ros.actions.Node(
package='ros_serial2wifi',
executable='tcp_server',
parameters= [{'serial_port' : '/tmp/tty_laser'}],
output='screen'
)
ydlidar = launch.actions.IncludeLaunchDescription(
PythonLaunchDescriptionSource(
[ydlidar_ros2_dir, '/launch', '/ydlidar_launch.py']),
)
ydlidar_delay = launch.actions.TimerAction(
period=5.0,
actions=[ydlidar]
)
return launch.LaunchDescription([
urdf2tf,
odom2tf,
microros_agent,
ros_serial2wifi,
ydlidar_delay
])
2、保存并重新构建功能包,运行该节点,接着给机器人重新上电,就可以建图了
3、使用slam_toolbox进行建图,在新的终端输入下面的指令
ros2 launch slam_toolbox online_async_launch.py use_sim_time:=False
打开Rviz,修改Fixed Frame为map,添加地图插件,就可以控制机器人进行建图了
4、使用nav2_map_server保存地图
在chapt9/fishbot_ws/src下新建功能包fishbot_navigation2,在功能包下新建maps目录,打开终端,进入maps目录,运行命令来保存地图
ros2 run nav2_map_server map_saver_cli -f room
1、在功能包fishbot_navigation2下创建config目录,然后将nav2_bringup提供的默认参数复制到config目录下
cp /opt/ros/$ROS_DISTRO/share/nav2_bringup/params/nav2_params.yaml src/fishbot_navigation2/config
2、在fishbot_navigation2功能包下创建launch目录,然后再该目录下新建navigation2.launch.py,将7.3.3节代码直接复制即可
3、修改CMakeLists.txt,添加launch、config、maps三个目录安装到install下,然后重新构建功能包
4、启动导航
ros2 launch fishbot_navigation2 navigation2.launch.py use_sim_time:=False