You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树莓派多进程控制风速计与伺服平台异常问题求助

树莓派多进程GPIO与伺服控制问题排查

问题背景

在树莓派项目中使用multiprocess模块实现两项核心功能:风速计风速计算、基于伺服电机的光伏板平台转动控制。主进程初始化硬件对象并启动三个子进程,其中calculate_power_p运行正常,但另外两个进程出现异常。

主函数代码:

def main():
    ane = Anemometer(pin=38, radius=65, anamometer_factor=1, action_time=5, 
                     measurment_fraquency=0.01, max_wind_speed=40, mesurment_time=30)
    prb = PhotoresistorBase(TR=1, TL=3, BR=0, BL=2, signifficant_diff=40)
    BS = Servo(pin=12, min_val=10, max_val=50)
    US = Servo(pin=18, min_val=29, max_val=43)
    pv_based = Pv(6)
    pv_servo = Pv(5)
    
    ...

    calculate_wind_vel_p = Process(target=calculate_wind_vel, args=(ane, BS, US))
    calculate_power_p = Process(target=calculate_power, args=(pv_based, pv_servo))
    turn_platform_p = Process(target=turn_platform, args=(prb, BS, US))

    calculate_wind_vel_p.start()
    calculate_power_p.start()
    turn_platform_p.start()

    calculate_wind_vel_p.join()
    calculate_power_p.join()
    turn_platform_p.join()

异常1:风速计进程calculate_wind_vel_p

目标函数代码:

def calculate_wind_vel(ane, BS, US):
    while True:
        wind_speed = ane.calculate_mean_wind_velocity()
        ...
        data = (datetime.datetime.now(), wind_speed)
        insert_vel_data(conn, data)
        time.sleep(1)
  • 多进程模式下,Anemometer类的calculate_wind_velocity方法中,count仅在第一次GPIO.event_detected触发时递增一次,后续事件触发不再计数;循环再次执行时count完全不递增。
  • 单独在主进程运行风速计代码时功能完全正常:
ane = Anemometer(pin=38, radius=65, anamometer_factor=1, action_time=5, 
                  measurment_fraquency=0.01, max_wind_speed=40, mesurment_time=30)
while True:
    ret = ane.calculate_mean_wind_velocity()
    print(ret)
    time.sleep(2)

异常2:伺服平台进程turn_platform_p

进程执行伺服类函数,但伺服电机无动作,函数未正确计算或改变状态。

问题根源

树莓派multiprocess采用fork机制创建子进程,子进程会复制主进程内存空间,但硬件资源存在独占性,导致两个核心问题:

  1. GPIO事件监听失效:主进程初始化的GPIO事件检测是内核级资源,不会被子进程复制,子进程无法捕获后续GPIO触发事件。
  2. 硬件对象跨进程无效:Anemometer、Servo等对象依赖的GPIO底层句柄在子进程中无效,无法正确操作硬件;同时多进程操作同一GPIO引脚会引发资源竞争,导致指令冲突。

解决方案

1. 改用多线程替代多进程(优先方案)

硬件控制场景下,多线程共享进程内存与硬件资源句柄,不会出现资源复制失效问题:

from threading import Thread
import time

def main():
    # 初始化所有硬件对象
    ane = Anemometer(pin=38, radius=65, anamometer_factor=1, action_time=5, 
                     measurment_fraquency=0.01, max_wind_speed=40, mesurment_time=30)
    prb = PhotoresistorBase(TR=1, TL=3, BR=0, BL=2, signifficant_diff=40)
    BS = Servo(pin=12, min_val=10, max_val=50)
    US = Servo(pin=18, min_val=29, max_val=43)
    pv_based = Pv(6)
    pv_servo = Pv(5)

    # 创建线程
    calculate_wind_vel_t = Thread(target=calculate_wind_vel, args=(ane, BS, US))
    calculate_power_t = Thread(target=calculate_power, args=(pv_based, pv_servo))
    turn_platform_t = Thread(target=turn_platform, args=(prb, BS, US))

    # 设置守护线程,随主进程退出
    calculate_wind_vel_t.daemon = True
    calculate_power_t.daemon = True
    turn_platform_t.daemon = True

    calculate_wind_vel_t.start()
    calculate_power_t.start()
    turn_platform_t.start()

    # 保持主进程运行
    while True:
        time.sleep(1)

2. 进程间队列传递指令(必须用多进程时)

通过multiprocessing.Queue实现主进程统一处理硬件操作,子进程仅负责计算逻辑:

from multiprocessing import Process, Queue
import datetime

def calculate_wind_vel(queue):
    while True:
        # 此处需从主进程获取GPIO计数或在子进程内独立初始化风速计(避免跨进程传递对象)
        wind_speed = ... 
        queue.put(('wind_data', wind_speed))
        time.sleep(1)

def turn_platform(queue):
    while True:
        # 计算平台转动角度指令
        angle_cmd = ...
        queue.put(('servo_cmd', 'BS', angle_cmd))
        time.sleep(0.5)

def main():
    queue = Queue()
    # 主进程初始化所有硬件对象
    ane = Anemometer(...)
    BS = Servo(...)
    US = Servo(...)
    pv_based = Pv(6)
    pv_servo = Pv(5)

    # 创建进程
    p1 = Process(target=calculate_wind_vel, args=(queue,))
    p2 = Process(target=turn_platform, args=(queue,))
    p3 = Process(target=calculate_power, args=(pv_based, pv_servo))

    p1.start()
    p2.start()
    p3.start()

    # 主进程监听队列,执行硬件操作
    while True:
        if not queue.empty():
            cmd_type, *args = queue.get()
            if cmd_type == 'wind_data':
                wind_speed = args[0]
                insert_vel_data(conn, (datetime.datetime.now(), wind_speed))
            elif cmd_type == 'servo_cmd':
                servo_name, angle = args
                if servo_name == 'BS':
                    BS.set_angle(angle)
        time.sleep(0.01)

3. 子进程内独立初始化硬件(避免资源冲突)

若坚持使用多进程,每个子进程需独立初始化对应硬件对象,禁止跨进程传递主进程创建的硬件实例:

def calculate_wind_vel():
    # 子进程内独立初始化风速计
    ane = Anemometer(pin=38, radius=65, anamometer_factor=1, action_time=5, 
                     measurment_fraquency=0.01, max_wind_speed=40, mesurment_time=30)
    while True:
        wind_speed = ane.calculate_mean_wind_velocity()
        data = (datetime.datetime.now(), wind_speed)
        insert_vel_data(conn, data)
        time.sleep(1)

def turn_platform():
    # 子进程内独立初始化光敏电阻与伺服电机
    prb = PhotoresistorBase(TR=1, TL=3, BR=0, BL=2, signifficant_diff=40)
    BS = Servo(pin=12, min_val=10, max_val=50)
    while True:
        # 执行平台转动逻辑
        ...

def main():
    calculate_wind_vel_p = Process(target=calculate_wind_vel)
    turn_platform_p = Process(target=turn_platform)
    calculate_power_p = Process(target=calculate_power, args=(pv_based, pv_servo))

    calculate_wind_vel_p.start()
    calculate_power_p.start()
    turn_platform_p.start()

    calculate_wind_vel_p.join()
    calculate_power_p.join()
    turn_platform_p.join()

注意:此方案需确保不同子进程不操作同一GPIO引脚,避免资源竞争。

内容的提问来源于stack exchange,提问作者Lezakk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 00:21:25