python机器人编程——开发一个pymatlab工具箱(上)

一、前言

我们知道matlab得工具箱用于分析数据非常强大,可以通过模块化得方式对数据进行处理,并可以有很多的工具模块,最后可以加入一个窗口,显示分析的结果。我们也知道,python的matplot绘图也非常强大,可以对各种维度的数据序列进行绘制,还有python有丰富的生态,有各行业的专业分析库,但是python需要编代码,对于工程人员使用起来有道门槛,难以实现像matlab的低代码分析应用。
本系列文章,试图打造一个python版的matlab,把机器人和控制、信号处理、图像处理相关的模块封装成流程图的块,并且打造跨进程通讯接口,从而实现便捷的应用。
本篇基本实现了对matplot的封装。效果如下:
在这里插入图片描述
在这里插入图片描述

二、实现过程

2.1 封装属性

matplot最常用的是曲线和散点、柱状图。我们把matplot封装到了一个叫MatplotPlotter的类里面,然后只要把数据整理成固定的形式,就可以调用这个类,然后显示出曲线,这个类把常用的属性给列出来了:

class MatplotPlotter:
    def __init__(self, fignum,
                 max_length=50,
                 maxframelen=1024,
                 viewwinlen=20,
                 plotlayout=111,
                 title="title",
                 xyzlabel=[['X','Y','Z']],                  
                 plottitles=["plot","scatter","plot"],
                 xyzlim_max=[],
                 isrealtime=True,
                 lineform=["plot","scatter","plot","bar"],
                 lineform3D=["plot3D","scatter3D","plot3D"],
                 colors=["blue",
                         "red",
                         "green",
                         "yellow",
                         "pink",
                         "brown",
                         "orange",
                         "purple",
                         "cyan",
                         "gray "
                         ],
                 size=(5, 4),
                 loc=(0,0)
                 ):
        self.cache = np.array([])
        self.max_length = max_length
        self.max_frame_length = maxframelen
        self.cache_length=0  
        self.size=size
        self.loc=loc
        self.title=title
        plt.style.use('dark_background')
        
        self.fig2D = None
        self.ax2D = None
        self.an2d=None
        self.fig3D = None
        self.ax3D = None
        self.an3d=None
        self.isshow=False
        self.viewwinlen=viewwinlen
        self.fignum=fignum
        self.plotlayout=plotlayout
        self.xyzlabel=xyzlabel
        self.plottitles=plottitles
        self.isrealtime=isrealtime
        #lineform:bar、plot、hist、pie
           
        self.lineform=lineform
        self.lineform3D=lineform3D
        self.formstyle={
            "pie":"self.ax2D.pie",
            "plot":"self.ax2D.plot",
            "bar":"self.ax2D.bar",
            "scatter":"self.ax2D.scatter",
            "plot3D":"self.ax3D.plot",
            "scatter3D":"self.ax3D.scatter"
            
            }
        self.colors=colors
        self.steps=None

2.2 数据流化显示

如上2D和3D的散点图、柱状图、线图都封装进去了。然后是图的title,位置、大小的设置等。
还有个就是,matplot一般很难显示动态图,这里我们的图可以进行实时动态显示,后期可以实时读取数据,并显示在matplot中可查看实时趋势。
在这里插入图片描述
在这里插入图片描述

2.3 输入数据的适应性

这个模块,可以接受numpy的数据,通过读取numpy的shape类型,来控制曲线的数量,曲线的2D和3D显示,如下:

    #只有y值,(1,N)或(N,)单曲线
    data0 = np.array([1],dtype=float) 
    
    data00 = np.array([[[1]]],dtype=float) 
    
    data1 = np.array([[1,2]],dtype=float)  
    #有x、y值(2,N),单曲线
    data2 = np.array([[1], [2]],dtype=float)
    #有x,y,z,(3,N),单曲线    
    data3 = np.array([[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7]],dtype=float)
    
    #有x,y,(2,N,N),N根曲线
    data4 = np.array([[
            [ 1.,1.,  1.],
            ],

           [[ 2.,2.,  4.],
            ]

          ],dtype=float)
    
    #有x,y,z,(3,N,N),N跟曲线
    data5 = np.array([[
            [ 1.,1.,  1.],
            ],
            [
                [ 1.,1.,  1.],
                ],

           [[ 2.,2.,  4.],
            ]

          ],dtype=float)
 

这样,只要把数据进行预处理,就可以马上进行可视化了。

三、核心代码说明

3.1 设置缓存

设置一个缓存,可以持续接收数据流,并显示出来,同时如果超过缓存允许的长度,就把最早的数据舍掉,加入最新的数据。这里涉及到对输入data的shape的判断,对支持的shape进行解析,要考虑经可能多的shape存在。这个需要经过多种shape的输入测试。
然后,如果输入只有一维,也就是只有Y值得情况,就应该补上x得值,这里x得值用step得自然数补充。
同时,如果缓存中存储了数据,再输入数据时,需要经过numpy得拼接等操作。

    def add_data(self, data):  
            
        if data.shape[0]>0 and data.shape[0]<=3: 
            if len(data.shape)>3:
                return False
            else:
                
                if len(data.shape)==1:
                    #print("add data fail,len(data.shape) must at least >1")
                    data=data.reshape(len(data),1)
                    
                if len(data.shape)==2:
                    if data.shape[0]==1:
                        num=len(data[0])
                        if self.steps is None:
                            x= np.arange(num)                        
                        else:
                            x = self.steps+num                            
                                     
                        data = np.concatenate((x.reshape(1,num), data), axis=0)
                        self.steps=x
                        #print("self.steps",self.steps)
                                           
                    data = data.reshape(data.shape[0], data.shape[1],1)
                    
                if len(data.shape)==3 and data.shape[0]==1:
                    pass
                    else:
                        #print("add new data")
                        if len(self.cache.shape) == len(data.shape):
                            pass                       
                        else:
                            
                            return False
                    return True
           
                else:
                    #single curve
                    print("single curve")
                    return False

                    
            
        else:
            print("data.shape[0] out of range must be (0,3]")
            return False

3.2 随机信号

为了测试趋势曲线图,可以设置一个随机信号产生得模块,形成信号源,实际应用可以采集传感器中得信号,来冲当信号源。

def draw(plot,data):
    
    # 定义均值和标准差
    mu = 5
    sigma = 0.6       
        
    print(plot.add_data(data))
    
    if data.shape[0]<=1:
        if len(data.shape)==1:
            pass
            num=len(data)
            error=np.random.normal(mu, sigma, num)
            data[0]=error[0]
        elif len(data.shape)==3:
            num=data.shape[1]
            error=np.random.normal(mu, sigma, num).reshape(num,1)
            #error2 = np.random.normal(mu, sigma, num)
            for i in range(1,data.shape[2]):
                error1 = np.random.normal(mu, sigma, num).reshape(num,1)
                #error2 = np.random.normal(mu, sigma, num)
                error=np.concatenate((error1.reshape(num,1), error), axis=1)
            data[0]=error
            print(data)
        else:
            num=len(data[0])
            error=np.random.normal(mu, sigma, num)
            data[0]=error                
    elif data.shape[0]==2:       
        data[0]=data[0]+(data.shape[1])            
    
        
        num=len(data[1])
        if len(data.shape)==3:
            #2D mutple
            error=np.random.normal(mu, sigma, num).reshape(num,1)
            #error2 = np.random.normal(mu, sigma, num)
            for i in range(1,data.shape[2]):
                error1 = np.random.normal(mu, sigma, num).reshape(num,1)
                #error2 = np.random.normal(mu, sigma, num)
                error=np.concatenate((error1.reshape(num,1), error), axis=1)
            data[1]=error
            #print("data4:",data4[0])
        elif len(data.shape)==2:
            #2D
            error=np.random.normal(mu, sigma, num).reshape(num,1)
            data[1]=error
        
    elif data.shape[0]==3:
        data[0]=data[0]+(data.shape[1])  
        data[1]=data[1]+(data.shape[1])
        
        num=len(data[2])
        if len(data.shape)==3:
            #3D mult
            error=np.random.normal(mu, sigma, num).reshape(num,1)
            #error2 = np.random.normal(mu, sigma, num)
            for i in range(1,data.shape[2]):
                error1 = np.random.normal(mu, sigma, num).reshape(num,1)
                #error2 = np.random.normal(mu, sigma, num)
                error=np.concatenate((error1.reshape(num,1), error), axis=1)
            data[2]=error
        elif len(data.shape)==2:
            #3D single
            error=np.random.normal(mu, sigma, num)
            data[2]=error

    plot.plot()

3.3 根据设置绘图

当存入缓存后,绘图得任务就是解析缓存中得数据,把数据根据颜色、标题、坐标最大最小、绘制得类型等绘制出来。

    def plot(self):
        number_str = str(self.plotlayout)                
        if len(number_str) == 3:                    
            hundreds = int(number_str[0])
            tens = int(number_str[1])
            units = int(number_str[2])
        if hundreds*tens != units:
            return False
        
        if len(self.cache.shape)==3:
            if self.plotlayout%10 != self.cache.shape[2] and self.plotlayout%10 != 1:
                return False
        if len(self.cache.shape)==2:
            #single line
            self.cache=self.cache.reshape(self.cache.shape[0],self.cache.shape[1],1)
        if units==1:            
            if self.cache.shape[0] == 2:  # 1D data  
                if self.fig2D == None:   
                    self.fig2D = plt.figure(self.fignum,figsize=self.size)
                    self.fig2D.canvas.manager.window.move(self.loc[0],self.loc[1])
                    
                if self.ax2D is None:
                    #nrows, ncols, index                            
                    self.ax2D = self.fig2D.add_subplot(111) 
                    self.ax2D.grid(linestyle='--', alpha=0.5)
                    
                    self.ax2D.set_xlabel("X")
                    self.ax2D.set_ylabel("Y")
                    
                self.ax2D.set_title(self.title)    
                   
                    
    
                if self.an2d is not None:
                    #self.an2d.pop().remove()
                    #print(len(self.an2d))
                    if isinstance(self.an2d, matplotlib.container.BarContainer):
                        self.an2d.remove()
                    else:                        
                        self.an2d.pop().remove()
                maxx=float('-inf')
                minx=float('inf')
                for line in range(self.cache.shape[2]): 
                    x = self.cache[0,:,line]
                    y = self.cache[1,:,line]
                    if len(x)>self.viewwinlen:
                        x=x[-self.viewwinlen:]
                        y=y[-self.viewwinlen:]
                        pass

五、总结

实现了这个模块后,就可以封装到工作流中,可以供以后数据分析监视使用。有了这个块,就可以绘制多个图,在屏幕得任意位置,便于数据分析,和实时监控,特别适合试验室监视。
当然,可以看出matplot这个画布如果用于实时显示,可以看见开销和实时性较大。用于研究和原型开发足够,但是用于实时得生产环境,还时考虑进行优化。

四、源码

已经上传到资源,下载链接

[------------本篇完--------------------------]

PS.扩展阅读

————————————————————————————————————————

对于python机器人编程感兴趣的小伙伴,可以进入如下链接阅读相关咨询

ps1.六自由度机器人相关文章资源

(1) 对六自由度机械臂的运动控制及python实现(附源码)
在这里插入图片描述

(2) N轴机械臂的MDH正向建模,及python算法
在这里插入图片描述

ps2.四轴机器相关文章资源

(1) 文章:python机器人编程——用python实现一个写字机器人
在这里插入图片描述

在这里插入图片描述

(2)python机器人实战——0到1创建一个自动是色块机器人项目-CSDN直播

(3)博文《我从0开始搭建了一个色块自动抓取机器人,并实现了大模型的接入和语音控制-(上基础篇)》的vrep基础环境
(3)博文《我从0开始搭建了一个色块自动抓取机器人,并实现了大模型的接入和语音控制-(上基础篇)》的vrep基础环境
(4)实现了语音输入+大模型指令解析+机器视觉+机械臂流程打通
在这里插入图片描述
在这里插入图片描述

ps3.移动小车相关文章资源

(1)python做了一个极简的栅格地图行走机器人,到底能干啥?[第五弹]——解锁蒙特卡洛定位功能-CSDN博客
(2) 对应python资源:源码地址
在这里插入图片描述
在这里插入图片描述

(3)python机器人编程——差速AGV机器、基于视觉和预测控制的循迹、自动行驶(上篇)_agv编程-CSDN博客
(4)python机器人编程——差速AGV机器、基于视觉和预测控制的循迹、自动行驶(下篇)_agv路线规划原则python-CSDN博客
对应python及仿真环境资源:源码链接
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

机智新语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值