前言

在做项目的时候,不同的程序负责不同的功能.但同时他们之间又是耦合的,一个程序的运行可能依赖于另一个程序的结果.以前都是基于ROS来完成这一工作,不过个人觉得ROS太笨重了,所以一直在找Python中有没有其它的工具能够实现同样的功能.经过各种搜索,自己稀里糊涂的用Redis完成了这一功能.

关于Redis能否像ROS一般的使用,我一直抱有疑问.但是当我在github上看到这个工作时,决定就用Redis的发布订阅功能来实现Python程序之间的通信.

Redis的安装与使用

Windows

安装redis,启动服务

去https://github.com/microsoftarchive/redis/releases下载zip安装包,解压即可。
然后将对应的路径添加到环境变量path中,就可以在cmd中使用命令redis-server启动redis服务了

使redis成为Windows服务

由于上面虽然启动了redis服务,但是,只要一关闭cmd窗口,redis服务就关闭了。所以,把redis设置为一个windows服务。

Linux

TO DO

python使用redis服务

python使用redis服务时需要先安装redis包,pip install redis

具体实现

首先实现一个通用的类,用于实现发布订阅功能,内容如下:

import cv2
import redis, struct
import json
import numpy as np


def toRedisformat(data, datatype):
    """encode data to string format"""
    if datatype == 'nparray':
        h, w = data.shape[:2]
        shape = struct.pack('>II', h, w)
        encoded = shape + data.tobytes()
    else:
        encoded = json.dumps(data)

    return encoded


def fromRedisFormat(data, datatype):
    """decode data"""
    if datatype == 'nparray':
        h, w = struct.unpack('>II', data[:8])
        data = np.frombuffer(data, dtype=np.uint8, offset=8).reshape(h, w, 3)
    else:
        data = json.loads(data)

    return data


class RedisCommunicator():
    """使用Redis进行发布订阅的类"""
    def __init__(self, R, chn, datatype=None):
        """
        初始化操作
        :param R: Redis的池
        :param chn: 通信的channel或者说话题
        """
        self._Redis = R
        self.chn = chn
        self.subInited = False
        self.datatype = datatype

    def pub(self, data):
        """发送信息,借助json"""
        data = toRedisformat(data, self.datatype)
        self._Redis.publish(self.chn, data)

        return True

    def sub(self):
        """接收信息,借助json"""
        if not self.subInited:
            """如果第一次接收,则初始化"""
            self.subInited = True

            self.subscriber = self._Redis.pubsub()  # 打开收音机
            self.subscriber.psubscribe(self.chn)     # 调频道
            self.subscriber.parse_response()        # 准备接收, 再次调用parse_response开始接收

        data = self.subscriber.get_message()
        ret = False
        if data:
            ret = True
            data = data['data']
            data = fromRedisFormat(data, self.datatype)

        return ret, data

其中,如果要存入Redis,内容必须字符串化.对于np array和其它类型数据,处理方式不同.处理np数组时,使用了struct包;处理其它数据类型时,使用了json包.
发布者的代码如下:

import redis
import cv2

from redis_utils import RedisCommunicator

RedisPool = redis.Redis(host='localhost', port=6379, db=0)

imgPubsub = RedisCommunicator(RedisPool, 'img', 'nparray')

cam = cv2.VideoCapture(0)
key = 0
while key != 27:
    ret, img = cam.read()

    imgPubsub.pub(img)

    cv2.imshow('puber', img)
    key = cv2.waitKey(1) & 0xFF

订阅者的代码如下:

import cv2
import redis

from redis_utils import RedisCommunicator

RedisPool = redis.Redis(host='localhost', port=6379, db=0)

imgPubsub = RedisCommunicator(RedisPool, 'img', 'nparray')

key = 0
while key != 27:
    ret, img = imgPubsub.sub()
    if ret:
        cv2.imshow('suber1', img)
    key = cv2.waitKey(1) & 0xFF

这样,就可以在不同的Python程序之间交换opencv的图像变量了,其它类型的数据同理.

参考资料

win10下安装redis
windows下安装Redis并部署成服务