7. 斗牛

让 PiCrawler 变身愤怒的公牛!利用摄像头锁定红布并发起冲击!

运行代码

cd ~/picrawler/examples
sudo python3 7_bull_fight.py

查看画面

代码运行后,终端会显示如下提示:

No desktop !
* Serving Flask app "vilib.vilib" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:9000/ (Press CTRL+C to quit)

然后在浏览器中输入 http://<your IP>:9000/mjpg 即可查看视频画面,例如: https://192.168.18.113:9000/mjpg

../_images/display.png

代码

备注

你可以对以下代码进行 Modify/Reset/Copy/Run/Stop 操作。但在此之前,需要进入源码路径,如 picrawler\examples。修改后可直接运行查看效果。

from picrawler import Picrawler
from time import sleep, time
from robot_hat import Music
from vilib import Vilib

# Create robot and audio controller objects
crawler = Picrawler()
music = Music()

def main():
    # Start camera and enable preview window
    Vilib.camera_start(vflip=False, hflip=False)
    Vilib.display(local=False, web=True)

    # Enable red color detection
    Vilib.color_detect("red")

    speed = 80                  # Movement speed
    last_seen = False           # Indicates whether the red target was detected in previous loop
    last_beep = 0               # Timestamp of last sound playback
    BEEP_COOLDOWN = 1.0         # Minimum interval between sound effects (seconds)

    # Stand once before starting tracking
    crawler.do_step('stand', 40)
    sleep(1.0)

    try:
        while True:
            # Read detection result
            if Vilib.detect_obj_parameter.get('color_n', 0) != 0:

                # Get horizontal coordinate of detected red object
                coordinate_x = Vilib.detect_obj_parameter.get('color_x', 0)

                # Play sound effect with cooldown to avoid spamming
                now = time()
                if now - last_beep >= BEEP_COOLDOWN:
                    try:
                        music.sound_play_threading('./sounds/talk1.wav')
                    except Exception:
                        pass
                    last_beep = now

                # Steering logic based on horizontal position
                # Left side of image
                if coordinate_x < 100:
                    crawler.do_action('turn left', 1, speed)

                # Right side of image
                elif coordinate_x > 220:
                    crawler.do_action('turn right', 1, speed)

                # Center area → move forward
                else:
                    crawler.do_action('forward', 2, speed)

                last_seen = True
                sleep(0.05)

            else:
                # No red target detected

                # Stop movement only once when target is lost
                # This prevents repeated stand() calls that cause "push-up" effect
                if last_seen:
                    crawler.do_step('stand', 40)
                    last_seen = False

                sleep(0.15)

    except KeyboardInterrupt:
        # Stop program safely when Ctrl+C is pressed
        print("\nStop.")

    finally:
        # Cleanup section to avoid exit errors

        # Disable color detection
        try:
            Vilib.color_detect("close")
        except Exception:
            pass

        # Close camera safely
        try:
            Vilib.camera_close()
        except Exception:
            pass

        # Make the robot sit before exit
        try:
            crawler.do_step('sit', 40)
            sleep(1.0)
        except Exception:
            pass

if __name__ == "__main__":
    main()

工作原理

  1. 摄像头初始化

    Vilib.camera_start(vflip=False, hflip=False)
    Vilib.display(local=False, web=True)
    Vilib.color_detect("red")
    

    启动摄像头并启用网页预览。 同时开启红色目标检测功能。

    Vilib 会在后台持续处理图像帧, 并将检测结果存储在 detect_obj_parameter 中。

  2. 机器人准备

    crawler.do_step('stand', 40)
    sleep(1.0)
    

    在开始目标跟踪之前, 机器人先执行站立动作。

    短暂的延时用于确保机器人姿态稳定。

  3. 检测目标

    if Vilib.detect_obj_parameter.get('color_n', 0) != 0:
        coordinate_x = Vilib.detect_obj_parameter.get('color_x', 0)
    

    程序首先检查是否检测到红色物体。

    如果检测到目标, 则读取该红色物体在图像中的水平中心坐标(x 位置)。

  4. 转向决策逻辑

    if coordinate_x < 100:
        crawler.do_action('turn left', 1, speed)
    elif coordinate_x > 220:
        crawler.do_action('turn right', 1, speed)
    else:
        crawler.do_action('forward', 2, speed)
    

    图像在水平方向被划分为三个区域: 左侧、中间和右侧。

    • 左侧区域 → 向左转

    • 右侧区域 → 向右转

    • 中间区域 → 向前移动

    通过这种方式,机器人可以持续跟随红色目标。

  5. 声音冷却机制

    now = time()
    if now - last_beep >= BEEP_COOLDOWN:
        music.sound_play_threading('./sounds/talk1.wav')
        last_beep = now
    

    通过设置冷却计时器, 可以防止声音被连续重复播放。

    即使目标持续被检测到, 声音效果也最多每秒播放一次。

  6. 目标丢失处理

    if last_seen:
        crawler.do_step('stand', 40)
        last_seen = False
    

    当红色目标消失时, 机器人会停止移动并恢复到稳定的站立姿态。

    last_seen 标志确保 stand() 只会执行一次, 从而避免重复复位姿态导致机器人抖动。

  7. 安全退出与资源清理

    finally:
        Vilib.color_detect("close")
        Vilib.camera_close()
        crawler.do_step('sit', 40)
    

    当程序退出时(例如按下 Ctrl+C):

    • 关闭颜色检测功能

    • 安全关闭摄像头

    • 机器人执行坐下动作

    这样可以避免摄像头错误, 并防止程序异常退出导致系统状态不稳定。