全国大学生智能汽车竞赛--讯飞组解读(四)

2026.01.13

语音启动

官方给出的语音启动方法是到官网注册账号然后获取语音识别API。由于过程比较复杂,而且会出现报错问题,所以我取巧利用麦克风回调函数来完成语音启动。

将官方的原始功能包ucar_ws成功编译不报错后,我们启动麦克风功能包speech_command中的launch文件。

roslaunch speech_command speech_command.launch

启动成功后,终端输出如下

图片走丢了...
麦克风启动

注意到最后两行提示语法构建错误,这是因为没有配置讯飞官方的语音api。当然你也可以去官方创建语音识别应用获取api,具体方法可以参照讯飞大群中F&Q问题清单文件。

对准小车麦克风说出唤醒词“小飞小飞”,如果看到以下输出说明麦克风可以正常使用。

图片走丢了...
语音唤醒输出

我们可以观察到,这段输出描述了刚才语音唤醒的一些基本信息,比如角度、时间等等。输出不会凭空产生,一定是有相关的代码去控制输出。所以我们就可以去找到对应代码,加以修改将它变为我们程序的入口。

我们在vscode搜索栏中输入%就可以进行整个目录下的详细搜索,搜索awake_angle,可以定位到AIUITester.cpp第953行:

if(reader.parse(info,root))
{
  angle = root["ivw"]["angle"].asFloat();
  cout << "awake_angle: "<< angle<<endl;
  if(result.length() > 10) {
    if(testCallback != NULL ) {
      testCallback();
    }
  }
}

可以发现,在输出awake_angle后进入了一个testCallback函数,该函数位于aiuiMain.cpp第220行,再尝试增加一个输出,看看唤醒后终端有无新变化。

void test_callback()
{
	get_request_test = true;
	cout << "修改语音回调函数成功!" << endl;
}

时刻牢记,修改c++文件必须在工作空间路径下执行catkin_make重新编译,否则修改无法生效。

可以看到终端有了新的输出,这代表我们找到了语音唤醒的入口。

图片走丢了...
修改回调函数输出

比赛要求的语音唤醒格式一般为“小飞小飞,xxx”,而我们这里喊“小飞小飞”已经可以成功进入回调函数,所以“xxx”是什么就不重要了。

接下来你只需要在testCallback函数中增加你的程序开始逻辑即可。例如你可以在导航主程序中订阅启动话题,然后在testCallback函数中发布启动话题,这样只需要喊“小飞小飞”主程序就能启动。

这个方法可行性取决于比赛要求的语音唤醒格式,如果不以“小飞小飞”开头那么该方法就失效。需要另辟蹊径或者老老实实配置api。

摄像头启动

官方的摄像头包为ucar_camera,在src目录下默认给出一个摄像头测试脚本,它发布了一个名为/ucar_camera/image_raw的图像话题,启动之后其他节点可以订阅该话题查看图像。

直接查看图像

为了简单起见,我们编写一个摄像头测试程序,直接打开摄像头查看图像。首先在ucar_ws/src/ucar_camera/src路径下新建名为image_show.py的文件,具体代码如下:

#!/usr/bin/python3
# -*- coding: UTF-8 -*-
import cv2
def main():
    # 摄像头设备号,通常 /dev/video0
    device_path = 0  # 或者 "/dev/video0"
    cap = cv2.VideoCapture(device_path)
    # 设置分辨率
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
    if not cap.isOpened():
        print("无法打开摄像头")
        return
    print("按 'q' 键退出")
    while True:
        ret, frame = cap.read()
        if not ret:
            print("无法读取摄像头帧")
            break
        # 水平翻转
        frame = cv2.flip(frame, 1)
        # 显示图像
        cv2.imshow("Camera", frame)
        # 按 'q' 退出
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    # 释放资源
    cap.release()
    cv2.destroyAllWindows()
if __name__ == "__main__":
    main()

然后在当前路径下执行python3 image_show.py,你可以看到摄像头图像。

图片走丢了...
查看图像

话题订阅图像

这里我们将启动官方的ucar_camera.py,然后在自己的功能包下编写一个订阅节点来接收图像并显示。这将是我们编写的第一个节点!

首先,进入/ucar_ws/src路径,执行:

catkin_create_pkg benz rospy std_msgs sensor_msgs cv_bridge

这里功能包的名字定义为benz,你可以随意取想要的名字。

然后在/benz/src下新建image_subscriber.py

#!/usr/bin/python3
# -*- coding: UTF-8 -*-
import rospy
from sensor_msgs.msg import Image
import numpy as np
import cv2
from cv_bridge import CvBridge

class ImageSubscriber:
    def __init__(self):
        rospy.init_node("image_subscriber", anonymous=True)

        # 订阅话题
        self.topic_name = rospy.get_param('~cam_topic_name', '/ucar_camera/image_raw')
        self.sub = rospy.Subscriber(self.topic_name, Image, self.callback)

        # cv_bridge 用于 ROS Image <-> OpenCV 转换
        self.bridge = CvBridge()

        rospy.loginfo(f"Subscribed to {self.topic_name}")
        rospy.spin()  # 阻塞,持续接收消息

    def callback(self, msg):
        try:
            # ROS Image -> OpenCV Image
            cv_image = self.bridge.imgmsg_to_cv2(msg, desired_encoding='bgr8')

            # 水平翻转并显示
            cv_image = cv2.flip(cv_image, 1)
            cv2.imshow("Ucar Camera", cv_image)

            # 必须调用 waitKey 才能刷新窗口
            if cv2.waitKey(1) & 0xFF == ord('q'):
                rospy.signal_shutdown("User requested exit")
                cv2.destroyAllWindows()

        except Exception as e:
            rospy.logerr(f"Error converting ROS Image to OpenCV: {e}")

if __name__ == "__main__":
    ImageSubscriber()

接着在/benz/src路径下为该python脚本添加可执行权限。

chmod +x image_subscriber.py

然后在工作空间路径下执行catkin_make重新编译。编译成功后,新建一个终端执行roscore启动ROS。

新建一个终端启动官方的图像发布节点:

rosrun ucar_camera ucar_camera.py 

注意请先将ucar_camera.py中对图像的分辨率设置为800*600,否则会报错。

self.img_width=int(rospy.get_param('~image_width',default=800))
self.img_height=int(rospy.get_param('~image_height',default=600))

新建一个终端启动自己编写的图像订阅节点,成功则可以正常看到图像!

rosrun ucar_camera ucar_camera.py 

如果显示出的图像颜色不对,说明是rgb和brg反了,将接收方或者发布方的颜色编码改一下就行。

图片走丢了...
回调函数接收图像

小结

本节内容主要是初步体验麦克风和摄像头。通过改写麦克风回调函数,你就可以实现语音启动,相当于完成了比赛的第一个任务。

摄像头有以下两种使用方式:

  • 在程序中直接打开摄像头,这样做的好处在于可以避免图像在ros节点间传递造成耗时(虽然感觉没有太大影响)。比如,你可以在图像识别节点直接打开摄像头读取图像然后进行处理,这应该会比订阅再识别快一些。

  • 一个节点专职发布图像,其他若干节点订阅图像并在回调函数中处理。其优势在于,可以高频率的进入回调函数。比如你有一个全局变量需要实时去检测或者维护,就可以放在图像回调函数中。同理,激光雷达的回调频率也很高,也可以去处理一些实时逻辑。


更多关于全国大学生智能汽车的分享,请访问个人主页。 期待你的评论与交流!