【ROS实战】03-从零实现小车运动控制的 ROS 功能包

本教程将从 零基础 带你创建一个包含两个节点的 ROS 包:
一个用键盘控制小车方向,另一个接收控制信息并解析动作。

本章使用ROS Noetic版本,如果对ROS背景,ROS主题,ROS架构和工作空间概念不了解的读者,可以先阅读我的专栏中的前两篇文章《01-ROS安装详细指南》和《02-ROS架构介绍》


🧱 第一步:创建工作空间与包

✅ 创建工作空间

这里我们给工作空间起名为catkin_ws

你可以根据需要命名工作空间。catkin_ws 是"catkin workspace"(catkin工作空间)的缩写,其中,catkin 是ROS中的构建系统的名称,ws 代表工作空间workspace。

mkdir -p ~/catkin_ws/src
cd ~/catkin_ws
catkin_make
source devel/setup.bash

✅ 创建 ROS 包

cd ~/catkin_ws/src
catkin_create_pkg my_robot_control rospy std_msgs geometry_msgs

解释:

  • my_robot_control:你的包名;
  • rospy:使用 Python 写 ROS 节点;
  • std_msgsgeometry_msgs:标准消息类型,geometry_msgs/Twist 是控制速度的关键消息类型。

✍️ 第二步:编写 Python 节点

1️⃣ keyboard_control_node.py:读取键盘输入,发布 /cmd_vel

路径~/catkin_ws/src/my_robot_control/scripts/keyboard_control_node.py

#!/usr/bin/env python3
import rospy
from geometry_msgs.msg import Twist

def main():
    # 创建一个Publisher,发布到 /cmd_vel,消息类型为Twist
    pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)
    # 初始化节点,名称为 keyboard_control_node
    rospy.init_node('keyboard_control_node')
    rate = rospy.Rate(10)  # 发布频率 10Hz

    print("=== 使用键盘控制小车 ===")
    print("w: 前进 | s: 后退 | a: 左转 | d: 右转 | q: 退出")

    while not rospy.is_shutdown():
        key = input("请输入方向键:")
        msg = Twist()  # 初始化速度消息
        if key == 'w':
            msg.linear.x = 0.5  # 前进
        elif key == 's':
            msg.linear.x = -0.5  # 后退
        elif key == 'a':
            msg.angular.z = 0.5  # 左转
        elif key == 'd':
            msg.angular.z = -0.5  # 右转
        elif key == 'q':
            print("退出控制程序")
            break
        else:
            print("无效输入,请输入 w/s/a/d/q")
            continue

        pub.publish(msg)  # 发布消息
        rate.sleep()

if __name__ == '__main__':
    main()

2️⃣ motor_controller_node.py:接收 /cmd_vel,输出控制信息

路径~/catkin_ws/src/my_robot_control/scripts/motor_controller_node.py

#!/usr/bin/env python3
import rospy
from geometry_msgs.msg import Twist

# 回调函数,当接收到 /cmd_vel 消息时触发
def callback(msg):
    linear = msg.linear.x
    angular = msg.angular.z

    # 判断动作类型
    if linear > 0:
        action = "前进"
    elif linear < 0:
        action = "后退"
    elif angular > 0:
        action = "左转"
    elif angular < 0:
        action = "右转"
    else:
        action = "停止"

    # 输出动作和速度信息
    print(f"[MotorController] 动作: {action} | 线速度: {linear:.2f} m/s | 角速度: {angular:.2f} rad/s")

def main():
    rospy.init_node('motor_controller_node')  # 初始化节点
    rospy.Subscriber('/cmd_vel', Twist, callback)  # 订阅 /cmd_vel
    rospy.spin()  # 等待回调

if __name__ == '__main__':
    main()

🔑 第三步:配置可执行权限与 CMakeLists.txt

✅ 设置可执行权限

chmod +x ~/catkin_ws/src/my_robot_control/scripts/*.py

✅ 修改 CMakeLists.txt

确保添加如下内容来安装你的 Python 脚本,修改~/catkin_ws/src/my_robot_control/CMakeLists.txt,在find_package之后添加如下语句

catkin_package()

catkin_install_python(PROGRAMS
  scripts/keyboard_control_node.py
  scripts/motor_controller_node.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

🔨 第四步:编译项目

cd ~/catkin_ws
catkin_make
source devel/setup.bash

🧪 第五步:单独运行每个节点

✅ 先启动 roscore

roscore

如果没有安装或者安装后命令无法识别,请参考我专栏中的《01-ROS安装详细指南》

✅ 运行 keyboard 控制节点

source ~/catkin_ws/devel/setup.bash
rosrun my_robot_control keyboard_control_node.py

含义解释:

  • rosrun:运行单个 ROS 节点;
  • my_robot_control:你的包名;
  • keyboard_control_node.py:脚本名称,必须在 scripts/ 下并有可执行权限。

✅ 在另一个终端运行 motor controller 节点

source ~/catkin_ws/devel/setup.bash
rosrun my_robot_control motor_controller_node.py

此时,在keyboard控制节点终端输入方向按键(wsad)并输入回车后,就可以看到另一个终端的输出信息。


🚀 第六步:使用 Launch 文件同时启动两个节点

目前每个节点都需要独立运行,当节点数量多了后,尤其是生产环境,可能有几十个甚至数百个节点,如果还是这样一个一个运行,就会非常麻烦,且启动的参数也不容易文档化管理。

这时,我们就可以使用Launch文件同时启动多个节点。

✅ 创建 launch 文件夹

mkdir -p ~/catkin_ws/src/my_robot_control/launch

✅ 编写 my_robot_launch.launch

路径~/catkin_ws/src/my_robot_control/launch/my_robot_launch.launch

<launch>
  <!-- 启动键盘控制节点 -->
  <node pkg="my_robot_control" type="keyboard_control_node.py" name="keyboard_control" output="screen"/>
  
  <!-- 启动电机控制节点 -->
  <node pkg="my_robot_control" type="motor_controller_node.py" name="motor_controller" output="screen"/>
</launch>
参数说明:
  • pkg:节点所在的包名;
  • type:节点的可执行文件(脚本)名称;
  • name:运行时给节点起的名字(可用于调试);
  • output="screen":将日志输出显示在终端。

✅ 启动 launch 文件

roslaunch my_robot_control my_robot_launch.launch

含义解释:

  • roslaunch:用于运行 launch 文件,支持同时启动多个节点;
  • my_robot_control:包名;
  • my_robot_launch.launch:启动文件名称,必须放在 launch/ 文件夹下。

此时,已经在一个窗口中通过roslaunch命令同时启动了两个节点,我们在窗口中同时输入控制指令并看到另一个节点的输出。


📂 最终项目结构

my_robot_control/
├── CMakeLists.txt
├── package.xml
├── launch/
│   └── my_robot_launch.launch
├── scripts/
│   ├── keyboard_control_node.py
│   └── motor_controller_node.py

常用调试命令

通过结合以下 ROS 相关命令,可以从另一个角度观察程序的运行状态。

在启动两个节点后:

  1. 查看当前话题:
    使用 rostopic list 命令可以列出当前系统中所有的话题,确以看到我们两个节点使用的 /cmd_vel 话题。

    $:~/catkin_ws$ rostopic list
    /cmd_vel
    
  2. 查看当前节点:
    使用 rosnode list 命令可以列出当前运行的所有节点,可以看到我们启动的两个节点:keyboard_controlmotor_controller

    $:~/catkin_ws$ rosnode list
    /keyboard_control
    /motor_controller
    
  3. 实时监听话题数据:
    使用 rostopic echo /cmd_vel 可以在终端中持续监听 /cmd_vel 话题的数据。如果该话题有数据发布,命令行中会实时打印出来。比如在执行后,在之前启动的 keyboard_control 终端中,输入 wsad 控制时,便能同步看到输出数据。

    $:~/catkin_ws$ rostopic echo /cmd_vel
    linear: 
     x: 0.5
     y: 0.0
     z: 0.0
    angular: 
     x: 0.0
     y: 0.0
     z: 0.0
    

这样,你就可以通过这些命令更直观地调试和监控 ROS 系统的运行状态。


✅ 小结

你现在拥有了一个完整的 ROS 包:

  • ✅ 可用键盘控制小车;
  • ✅ 发布 /cmd_vel 控制速度;
  • ✅ 模拟电机接收速度并输出动作信息;
  • ✅ 支持手动运行和 launch 一键启动!
  • ✅ 使用ros的常用命令进行调试。

如果想继续扩展,可以加入:

  • 🔋 控制真实电机(通过串口或 GPIO);
  • 🖥️ 用 GUI 或者接受远端遥控信号替代命令行键盘输入;
  • 🧠 使用导航算法,实现自动规划。
Python中使用ROS (Robot Operating System) 控制小车运动并让其轨迹呈现特定形状如"MKP",首先你需要安装必要的库,比如`rospy`, `geometry_msgs`, 和 `control_toolbox` 等。这里是一个简化的示例,假设你已经有一个基本的ROS节点运行,并且小车运动是通过` Twist`消息控制的。 ```python import rospy from geometry_msgs.msg import Pose, Twist from control_toolbox import linearize, pid # 定义PID控制器 kp = 0.5 # 比例增益 ki = 0.0 # 积分增益 kd = 0.0 # 微分增益 pid_controller = pid.PID(kp, ki, kd) # 设定目标位置 goal_pose = Pose(position=[x_mkp, y_mkp]) # x_mkp和y_mkp是"MKP"路径的关键点坐标 def track_path(): # 创建Publisher发布 Twist 控制信号 cmd_vel_pub = rospy.Publisher('cmd_vel', Twist, queue_size=10) rate = rospy.Rate(10) # 设置更新频率 while not rospy.is_shutdown(): current_pose = get_current_pose() # 获取当前小车位置函数 error = goal_pose.position - current_pose.position # 计算误差 twist_msg = Twist() # PID控制器计算控制量 vel_x = pid_controller.update(error.x) vel_y = pid_controller.update(error.y) # 将控制量转换为实际速度 twist_msg.linear.x = vel_x twist_msg.linear.y = vel_y # 发布Twist消息 cmd_vel_pub.publish(twist_msg) rate.sleep() def get_current_pose(): # 这里需要实现ROS获取当前小车位置的功能,这通常通过订阅Topic完成 # 例如: # current_pose = rospy.wait_for_message('/odom', Odometry).pose.pose pass if __name__ == '__main__': rospy.init_node('path_tracking_node') try: track_path() except rospy.ROSInterruptException: pass
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值