ROS 实战:从零搭建你的第一个机器人

ROS
4.2k 词

从 URDF 建模到 Gazebo 仿真、控制器驱动、TF 坐标变换,完整走一遍搭建小车和机械臂的流程。

一、搭建四轮小车

启动命令速查

1
2
3
4
5
6
7
8
# Gazebo 仿真
roslaunch urdf_sim_tutorial gazebo.launch model:=rbo.urdf.xacro

# RViz 可视化
roslaunch urdf_sim_tutorial display.launch model:=rbo.urdf.xacro

# 发布速度指令
rostopic pub -r 30 /cmd_vel geometry_msgs/Twist -- '[1.0,0,0]' '[0,0,0.0]'

轮子建模(xacro 复用)

四个轮子结构完全相同,用 xacro 宏避免重复:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<xacro:macro name="wheel" params="name x y z">
<link name="wheel_${name}">
<visual>
<geometry>
<cylinder length="${wheel_length}" radius="${wheel_radius}"/>
</geometry>
<material name="Black"/>
</visual>
<collision>
<geometry>
<cylinder length="${wheel_length}" radius="${wheel_radius}"/>
</geometry>
</collision>
<inertial>
<mass value="0.5"/>
<inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001"/>
</inertial>
</link>

<joint name="joint_wheel_${name}" type="continuous">
<parent link="base_link"/>
<child link="wheel_${name}"/>
<origin xyz="${x} ${y} ${z}" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
</xacro:macro>

<!-- 使用 -->
<xacro:wheel name="LF" x="0.15" y="0.12" z="-0.05"/>
<xacro:wheel name="RF" x="0.15" y="-0.12" z="-0.05"/>
<xacro:wheel name="LR" x="-0.15" y="0.12" z="-0.05"/>
<xacro:wheel name="RR" x="-0.15" y="-0.12" z="-0.05"/>

编写速度控制器

通过发布话题控制四轮速度:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <ros/ros.h>
#include <std_msgs/Float64MultiArray.h>

int main(int argc, char **argv) {
ros::init(argc, argv, "command_publisher");
ros::NodeHandle n;
ros::Publisher pub = n.advertise<std_msgs::Float64MultiArray>(
"/joint_group_velocity_controller/command", 10);
ros::Rate loop_rate(10);

while (ros::ok()) {
std_msgs::Float64MultiArray msg;
msg.data = {1.0, 1.0, 1.0, 1.0}; // 四轮同速前进
pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
}
}

二、搭建机械臂(硬件接口示例)

与小车不同,机械臂需要通过硬件接口与真实/仿真电机通信。

硬件接口头文件骨架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <hardware_interface/joint_command_interface.h>
#include <hardware_interface/joint_state_interface.h>
#include <hardware_interface/robot_hw.h>
#include <controller_manager/controller_manager.h>

class ArmHWInterface : public hardware_interface::RobotHW {
public:
ArmHWInterface();
void read(const JointStateConstPtr &msg);
void publishCmd();

private:
hardware_interface::JointStateInterface jnt_state_interface;
hardware_interface::PositionJointInterface jnt_cmd_interface;
double cmd[5], pos[5], vel[5], eff[5];
};

关键流程

  1. 继承 hardware_interface::RobotHW
  2. 注册 JointStateInterface(状态读取)和 PositionJointInterface(指令下发)
  3. 实现 read() 从硬件/仿真读取关节状态
  4. 实现 write() 向硬件/仿真下发控制指令

三、ROS 与 STM32 通信

在机器人系统中,主控(树莓派/NUC)跑 ROS,STM32 负责底层电机控制。二者通信协议不同,需要串口转换芯片作为”翻译官”。

1
2
3
4
┌──────────┐    UART     ┌──────────────┐    CAN/GPIO    ┌──────────┐
│ ROS 主控 │ ←────────→ │ 串口转换芯片 │ ←───────────→ │ STM32 │
│ (NUC/Pi) │ │ (CH340/CP2102)│ │ (电机控制)│
└──────────┘ └──────────────┘ └──────────┘

软件层面,在 ROS 端通过串口节点收发数据即可。

四、TF2 坐标变换

TF2 管理机器人各部件之间的坐标系关系——知道激光雷达相对于底盘的位置、机械臂末端相对于基座的位置。

观察 TF 树

1
2
rosrun tf2_tools view_frames.py
evince frames.pdf

这会生成一张坐标系关系图,清晰展示各 link 的父子关系。

1
2
3
4
# 查看两个坐标系之间的实时变换
rosrun tf tf_echo [reference_frame] [target_frame]
# 例:
rosrun tf tf_echo base_link sensor_laser

小海龟 Demo

一个直观的 TF2 入门实验:

1
2
sudo apt-get install ros-$ROS_DISTRO-turtle-tf2 ros-$ROS_DISTRO-tf2-tools ros-$ROS_DISTRO-tf
roslaunch turtle_tf2 turtle_tf2_demo.launch

第二只海龟会自动跟随你操控的那只——这就是 TF 坐标变换在起作用。

常见安装问题

1
2
3
4
5
6
7
8
# No module named yaml
sudo apt-get install python-yaml # 不是 python3-yaml!

# No module named rospkg
sudo apt install python-is-python3

# 其他
sudo apt-get install python3-catkin-pkg

五、从建模到运动——完整流程回顾

1
2
3
4
5
6
7
8
9
10
11
12
13
1. Fusion360/SolidWorks 建模

2. 导出 STL → 编写 URDF/xacro

3. 添加 Transmission + ros_control 插件

4. 编写控制器(速度/位置/力控)

5. 编写 launch 文件启动 Gazebo + RViz

6. rostopic pub 测试运动

7. 接入真实硬件(STM32 + 串口)