精华内容
下载资源
问答
  • web开发功能权限控制

    万次阅读 2016-02-19 11:19:50
    网站功能权限控制 在网站开发的过程中,经常会遇到让某些功能只提供给某些特定的用户去使用,比如,一些高级功能只提供给会员用户使用,而一般的普通用户只能访问一些简单的功能

      在网站开发的过程中,经常会遇到让某些功能只提供给某些特定的用户去使用,比如,一些高级功能只提供给会员用户使用,而一般的普通用户只能访问一些简单的功能。

    具体的实现方式如下。


    (1)前端控制:

      前端的控制比较简单,从后台获取到用户的权限之后,可以存在session或者cookie中,然后在页面加载的时候,通过session或者cookie中存的权限来选择让该功能展现或者禁用。


    (2)后台控制:

      仅仅依靠前端的控制是无法完美解决权限控制的问题,因为前端页面的加载过程是在浏览器中完成的,用户可以自行篡改页面;或者用户可以直接通过URI请求来获取非法权限功能。所以需要在后台实现权限控制。

      后台的控制方法也很多,比如filter、spring的AOP等。在此选用springMVC的interceptor来控制。

      首先,在appContext-mvc.xml配置文件中配置拦截器,如下所示:

    <mvc:interceptors>
        <!-- 会员功能 -->
        <mvc:interceptor>  
            <mvc:mapping path="/fund/mem/**" />
            <bean class="org.fund.user.interceptor.AuthInterceptor" />  
        </mvc:interceptor>
    </mvc:interceptors>  

      其中,path表示需要控制的接口路径,可以使用正则表达式来匹配。

      然后,需要自己定义一个拦截器类,继承HandlerInterceptor接口,然后在preHandle方法里进行权限判断,如下所示:

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        User user = UserHolder.getUser();
        if (user.getAuth() == GameIdeaType.VIP_USER.getId()) {
            return true;
        } else {
            return false;
        }
    }

      此时,当用户访问接口时,会首先进入这个拦截器的preHandle方法去处理。若权限通过则返回true;否则返回false,此次访问结束。

      到此,已经实现了权限的控制,但是无法在用户访问非法权限接口时给出提示。若要解决这个问题,需要添加一个全局异常管理。


    (3)全局异常管理:

      思路是在拦截器中权限校验失败时,抛出一个权限校验失败的异常,然后通过全局异常管理类来捕获并返回前端特定的格式。具体如下。

      首先,在配置文件中添加全局异常管理类,如下:

    <!-- 全局异常管理 -->
    <bean id="handlerExceptionResolver" class="org.fund.common.ExceptionHandler">
        <property name="order" value="0"></property>
    </bean>

      然后,定义异常管理类,实现SimpleMappingExceptionResolver接口。

    /**
     * 全局异常处理类
     * 
     * @author 
     */
    public class ExceptionHandler extends SimpleMappingExceptionResolver {
    
        @Override
        public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) {
            logger.info("System Error Occurred. " + e.getMessage(), e);
    
            ModelAndView model = new ModelAndView(new MappingJacksonJsonView());
            List<String> errors = new ArrayList<String>();
    
            if (e instanceof NoPermissionException) {
                errors.add("抱歉,该功能开通会员之后才能使用!");
            } else {
                errors.add("系统异常");
            }
    
            return paramError(model, errors);
        }
    
        private ModelAndView paramError(ModelAndView mv, List<String> errors) {
            mv.addObject("success", false);
            mv.addObject("data", null);
            mv.addObject("errors", errors);
            return mv;
        }
    
    }

      最后,修改拦截器的实现

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        User user = UserHolder.getUser();
        if (user.getAuth() == GameIdeaType.VIP_USER.getId()) {
            return true;
        } else {
            throw new NoPermissionException();
        }
    }
    展开全文
  • 访问控制第二层级:利用 Shiro 过滤器实现功能访问控制

    logo


    重要链接:
    「系列文章目录」

    「项目源码(GitHub)」

    前言

    最近又休了几天假。读者大人们是不是感觉我老是休假?说实话,在这个同事们都努力加班的时候,一开始我也有那么一丢丢不好意思,但休假也是为了以更好的精神状态为公司赚钱嘛(狗头)。

    大家出来工作,一定要记住身体是最重要的,透支身体换来的那点回报真的不值得。最近在知乎上看到,脑皮层会在夜间释放大量皮质醇(类似肾上腺素)代替多巴胺,使人进入一种警戒状态,这种神奇的状态被误认为是兴奋、是年轻的象征,其实对身体损伤很大。我看到后立马转发了朋友圈,不知道老板看到会不会想开除我。。。


    这篇文章的主要讲解 功能级访问控制 的实现方式。之所以要实现这个粒度的访问控制,是因为仅仅对菜单(页面)进行控制是不够的。

    举个例子,假设我们不想让 “内容管理员” 角色有查看用户列表的权限,可以通过对菜单的控制,让这个角色无法加载用户信息组件。但在会话持续状态下,该角色仍然可以向后台展示用户列表的接口发送请求,获取到所有的用户信息,这不就是掩耳盗铃儿响叮当嘛。

    为了实现功能控制,我们需要进行如下工作:

    • 设计数据库表(功能表与角色-功能表)
    • 完善新表对应的 pojo、DAO、service 类
    • 编写 shiro 过滤器并配置过滤条件

    为了展示这一篇的效果,我特意把如下几个后台页面完善了一下。

    运行情况(Dashboard):
    dashboard
    用户信息:
    用户信息
    角色配置:
    角色配置
    图书管理:

    图书管理
    (Dashboard 界面是从 「vue-element-admin」 项目中扒拉下来的,该项目有 45.9k stars ,作者是字节跳动的大神,强烈建议前端小伙伴学习)

    修改界面的过程就像画一匹马一样简单,我就不细讲了。
    怎样画马
    在正文之前,先提醒一下老读者,按上篇文章实现动态加载后,发现导航栏的点击高亮失效,经过排查,发现是路由守卫里多写了一个 next(),导致在访问需要登陆的页面时若认证成功则会执行两次 next(),影响了 el-menu 响应点击事件的方法。

    下面的代码中注释掉了错误的部分:

    router.beforeEach((to, from, next) => {
        if (store.state.user.username && to.path.startsWith('/admin')) {
          axios.get('/authentication').then(resp => {
            initAdminMenu(router, store)
          })
        }
        if (to.meta.requireAuth) {
          if (store.state.user.username) {
            axios.get('/authentication').then(resp => {
              if (resp) next()
            })
            // 就是这里多写了一个
            // next()
          } else {
            next({
              path: 'login',
              query: {redirect: to.fullPath}
            })
          }
        } else {
          next()
        }
      }
    )
    

    一、数据库与后端准备

    这个部分老生常谈了,我挑重点说哈。

    1.表设计

    本篇涉及到的表如下所示:
    在这里插入图片描述
    其实跟上篇的设计是类似的。嫌麻烦的话可以直接运行 wj.sql 文件。除了两张新建的表admin_permissionadmin_role_permission 外,还对 user 表的字段作了一些扩充(主要是为了列表充实一点好看。。。)

    解释一下权限表的设计:

    • name 即权限的名称,推荐使用英文
    • desc_ 即对权限功能的具体描述
    • url 即权限对应的接口,是实现功能控制的关键

    建完表记得加点数据,比如设置查询用户的权限,并赋给系统管理员角色,方便之后验证。

    2.Service

    pojo、DAO 还是那样相貌平平。

    AdminPermissionService 中需要实现一个根据当前用户获取所有权限的方法,与上节获取菜单列表不同,这里只需要 url 一个字段。代码如下:

    public Set<String> listPermissionURLsByUser(String username) {
        List<Integer> rids = adminRoleService.listRolesByUser(username)
                .stream().map(AdminRole::getId).collect(Collectors.toList());
    
        List<Integer> pids = adminRolePermissionDAO.findAllByRid(rids)
                .stream().map(AdminRolePermission::getPid).collect(Collectors.toList());
    
        List<AdminPermission> perms = adminPermissionDAO.findAllById(pids);
    
        Set<String> URLs = perms.stream().map(AdminPermission::getUrl).collect(Collectors.toSet());
    
        return URLs;
    }
    

    此外,还可以实现一个方法,用于判断用户请求接口的是否在权限列表中。如果没有对应权限,说明不需要维护。代码如下:

    public boolean needFilter(String requestAPI) {
        List<AdminPermission> ps = adminPermissionDAO.findAll();
        for (AdminPermission p: ps) {
            if (p.getUrl().equals(requestAPI)) {
                return true;
            }
        }
        return false;
    }
    

    同样为了演示效果,可以在 Controller 中编写一个查询所有用户接口:

    @GetMapping("/api/admin/user")
    public List<User> listUsers() throws Exception {
        return userService.list();
    }
    

    这样,准备工作就做充足了。

    二、Shiro 实现

    之前我们在做登录拦截的时候使用了拦截器,即 Interceptor。由于 Shiro 的权限机制要靠它自身提供的过滤器实现,所以我们现在弃用之前的拦截器。

    首先在 MyWebConfigurer 中删除拦截器配置代码:

    //    删了删了
    //    @Override
    //    public void addInterceptors(InterceptorRegistry registry) {
    //        registry.addInterceptor(getLoginInterceptor())
    //                .addPathPatterns("/**")
    //                .excludePathPatterns("/index.html")
    //                .excludePathPatterns("/api/register")
    //                .excludePathPatterns("/api/login")
    //                .excludePathPatterns("/api/logout")
    //                .excludePathPatterns("/api/books");
    //    }
    

    然后删除 LoginInterceptor 类(甚至可以直接删除 package)。做完这件事我神清气爽,因为在回答问题的过程中我发现你们遇到的跨域问题百分之八十都是这玩意儿没写好造成的。。。

    1.编写基于 URL 的过滤器

    为了让你们更好地理解 Shiro 过滤器是如何工作的,我们先来尝试自定义一个过滤器实现我们想要的功能。

    PathMatchingFilter 是 Shiro 提供的路径过滤器,我们可以通过继承它来编写过滤放行条件,即判断是否具有相应权限。判断的逻辑为:

    • 首先,判断当前会话对应的用户是否登录,如果未登录直接 false
    • 第二步,判断访问的接口是否有对应的权限,如果没有视为不需要权限即可访问,直接 true
    • 如果需要权限,查询出当前用户对应的所有权限,遍历并与需要访问的接口进行比对,如果存在相应权限则 true,否则 false

    OK,让我们新建一个 package 命名为 filter,编写 URLPathMatchingFilter 类如下:

    package com.gm.wj.filter;
    
    import com.gm.wj.service.AdminPermissionService;
    import com.gm.wj.util.SpringContextUtils;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.web.filter.PathMatchingFilter;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.HttpStatus;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.Set;
    
    public class URLPathMatchingFilter extends PathMatchingFilter {
        @Autowired
        AdminPermissionService adminPermissionService;
    
        @Override
        protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;
            // 放行 options 请求
            if (HttpMethod.OPTIONS.toString().equals((httpServletRequest).getMethod())) {
                httpServletResponse.setStatus(HttpStatus.NO_CONTENT.value());
                return true;
            }
    
            if (null==adminPermissionService) {
                adminPermissionService = SpringContextUtils.getContext().getBean(AdminPermissionService.class);
            }
    
            String requestAPI = getPathWithinApplication(request);
            System.out.println("访问接口:" + requestAPI);
    
            Subject subject = SecurityUtils.getSubject();
    
            if (!subject.isAuthenticated()) {
                System.out.println("需要登录");
                return false;
            }
    
            // 判断访问接口是否需要过滤(数据库中是否有对应信息)
            boolean needFilter = adminPermissionService.needFilter(requestAPI);
            if (!needFilter) {
                System.out.println("接口:" + requestAPI + "无需权限");
                return true;
            } else {
                System.out.println("验证访问权限:" + requestAPI);
                // 判断当前用户是否有相应权限
                boolean hasPermission = false;
                String username = subject.getPrincipal().toString();
                Set<String> permissionAPIs = adminPermissionService.listPermissionURLsByUser(username);
                for (String api : permissionAPIs) {
                    if (api.equals(requestAPI)) {
                        hasPermission = true;
                        break;
                    }
                }
    
                if (hasPermission) {
                    System.out.println("访问权限:" + requestAPI + "验证成功");
                    return true;
                } else {
                    System.out.println("当前用户没有访问接口" + requestAPI + "的权限");
                    return false;
                }
            }
        }
    }
    

    这里有一段代码解释一下:

    if (null==adminPermissionService) {
        adminPermissionService = SpringContextUtils.getContext().getBean(AdminPermissionService.class);
    }
    

    在 Shiro 的配置文件中,我们不能把 URLPathMatchingFilter@Bean 被 Spring 管理起来。 原因是 Shiro 存在 bug, 这个也是过滤器,ShiroFilterFactoryBean 也是过滤器,当他们都出现的时候,默认的什么 anno,authc 过滤器就失效了。所以不能把他声明为 @Bean

    因此,我们无法在 URLPathMatchingFilter 中使用 @Autowired 注入 AdminPermissionService 类,所以需要借助一个工具类利用 Spring 应用上下文获取 AdminPermissionService 的实例。

    工具类可以放在 utils 包中,代码如下:

    package com.gm.wj.util;
    
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    @Component
    public class SpringContextUtils implements ApplicationContextAware {
        private static ApplicationContext context;
    
        public void setApplicationContext(ApplicationContext context) throws BeansException {
            SpringContextUtils.context = context;
        }
    
        public static ApplicationContext getContext() {
            return context;
        }
    }
    
    

    接下来在配置类 ShiroConfiguration 增加获取过滤器的方法,注意这里不能使用 @Bean

        public URLPathMatchingFilter getURLPathMatchingFilter() {
            return new URLPathMatchingFilter();
        }
    

    然后编写 shiroFilter 配置方法如下:

    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
    
        Map<String, String > filterChainDefinitionMap = new LinkedHashMap<String, String>();
        Map<String, Filter> customizedFilter = new HashMap<>();
    
        // 设置自定义过滤器名称为 url
        customizedFilter.put("url", getURLPathMatchingFilter());
    
        // 对管理接口的访问启用自定义拦截(url 规则),即执行 URLPathMatchingFilter 中定义的过滤方法
        filterChainDefinitionMap.put("/api/admin/**", "url");
        // 启用自定义过滤器
        shiroFilterFactoryBean.setFilters(customizedFilter);
        filterChainDefinitionMap.put("/api/authentication", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    

    OK,这时候你可以测试一下,使用 editor 账号(密码 123)登录,由于获取不到对应权限,请求自动转发到 \login,不过由于我们是前后端分离的,所以\login 这个接口并不存在,可以通过 shiroFilterFactoryBean.setLoginUrl() 方法手动设置跳转路径,不过没有必要,我们在前端配置的规则会引发路由跳转到登录页面。

    上面的 filterChainDefinitionMap.put("/api/authentication", "authc"); 是我们的防前端鸡贼登录规则。

    这个 authc 即 autentication,是 shiro 自带的过滤器。除了它以外,常用的还有 anon(可匿名访问)、roles(需要角色)、perms(需要权限)等。

    讲到这里你可能有疑问,为啥我们不直接用 perms 呢?

    其实使用 perms 才是 Shiro 的祖传解决方案,但是为了配合它的实现,我们需要在配置文件中添加规则如filterChainDefinitionMap.put("/api/authentication", "perms[/api/admin/user]"),或者在接口处编写注解如 @RequirePermission("/api/admin/user") ,这样如果我们想要删除或新增权限,除了修改数据库外还需要重新编写源码,这就比较蓝瘦了。

    此外,自带过滤器会拦截 options 请求,所以在前后端分离的项目里使用自定义过滤器反而简便一些。。。

    不过,其实对很多项目来说不太需要动态增删权限,只需要对现有权限进行分配就够了,所以在不分离的情况下使用自带过滤器当然更好。

    2.祖传方法

    之前我们已经写好了 Service,接下来要做的事很简单:

    • 在 Realm 中配置授权信息
    • 为需要控制的接口添加注解
    • 编写异常处理类(统一处理未授权异常)

    在 WJRealm 中重写获取授权信息的方法如下:

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 获取当前用户的所有权限
        String username = principalCollection.getPrimaryPrincipal().toString();
        Set<String> permissions = adminPermissionService.listPermissionURLsByUser(username);
    
        // 将权限放入授权信息中
        SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
        s.setStringPermissions(permissions);
        return s;
    }
    

    我们选用注解的方式控制用户信息查询权限如下:

     @RequiresPermissions("/api/admin/user")
     @GetMapping("/api/admin/user")
     public List<User> listUsers() throws Exception {
         return userService.list();
     }
    

    权限的名字就可以灵活一些,不用与接口 url 一致,这里我就不改数据库了。

    最后,编写一个处理未授权异常的类,可以放在新的 package 中:

    package com.gm.wj.exception;
    
    import com.gm.wj.result.Result;
    import com.gm.wj.result.ResultFactory;
    import org.apache.shiro.authz.UnauthorizedException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @ControllerAdvice
    public class DefaultExceptionHandler {
        @ExceptionHandler(Exception.class)
        @ResponseBody
        public Result handleAuthorizationException(UnauthorizedException e) {
            String message = "权限认证失败";
            return ResultFactory.buildFailResult(message);
        }
    }
    
    

    为了验证效果,我们注释掉之前的 shiro 配置中关于自定义拦截器的部分。然后用 authc 过滤器保护后台所有接口,也就是说需要先登录再判断是否存在权限:

    
    filterChainDefinitionMap.put("/api/admin/**", "authc");
    // shiroFilterFactoryBean.setFilters(customizedFilter);
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    

    测试一下,用 editor 用户登录,访问用户信息接口

    接口测试
    之后项目里我们还是采用自定义过滤器的方法。

    下一步

    目前虽然两个层级的访问控制逻辑大概完成了,但还有很多累活需要做,比如用户信息修改、角色信息修改、角色分配、权限分配等等,这些背后的工作不是主线,如果有需要特别注意的地方我会在后面的文章提到。

    数据级访问控制的实现需要依托一些具体功能,所以在这之前会先写别的方面。

    本篇文章 Shiro 的部分主要参考自:How2J.cn - Shiro 系列教材

    另外由于提问的读者越来越多了,我决定中午 12 点到 12 点半集中精力回答,尽量提高一些效率。

    上一篇:Vue + Spring Boot 项目实战(十五):动态加载后台菜单

    下一篇:Vue + Spring Boot 项目实战(十七):后台角色、权限与菜单分配

    展开全文
  • PID控制详解

    万次阅读 多人点赞 2018-12-16 10:43:04
    PID( Proportional Integral Derivative)控制是最早发展起来的控制策略之一,由于其算法简单、鲁棒性好和可靠性高,被广泛应用于工业过程控制,尤其适用于可建立精确数学模型的确定性控制系统。 在工程实际中,...

    PID控制详解

    一、PID控制简介

       PID( Proportional Integral Derivative)控制是最早发展起来的控制策略之一,由于其算法简单、鲁棒性好和可靠性高,被广泛应用于工业过程控制,尤其适用于可建立精确数学模型的确定性控制系统。

       在工程实际中,应用最为广泛的调节器控制规律为比例、积分、微分控制,简称PID控制,又称PID调节,它实际上是一种算法。PID控制器问世至今已有近70年历史,它以其结构简单、稳定性好、工作可靠、调整方便而成为工业控制的主要技术之一。当被控对象的结构和参数不能完全掌握,或得不到精确的数学模型时,控制理论的其它技术难以采用时,系统控制器的结构和参数必须依靠经验和现场调试来确定,这时应用PID控制技术最为方便。即当我们不完全了解一个系统和被控对象,或不能通过有效的测量手段来获得系统参数时,最适合用PID控制技术。PID控制,实际中也有PI和PD控制。PID控制器就是根据系统的误差,利用比例、积分、微分计算出控制量进行控制的。

       从信号变换的角度而言,超前校正、滞后校正、滞后-超前校正可以总结为比例、积分、微分三种运算及其组合。

       PID调节器的适用范围:PID调节控制是一个传统控制方法,它适用于温度、压力、流量、液位等几乎所有现场,不同的现场,仅仅是PID参数应设置不同,只要参数设置得当均可以达到很好的效果。均可以达到0.1%,甚至更高的控制要求。

    PID控制的不足

      1. 在实际工业生产过程往往具有非线性、时变不确定,难以建立精确的数学模型,常规的PID控制器不能达到理想的控制效果;

      2. 在实际生产现场中,由于受到参数整定方法烦杂的困扰,常规PID控制器参数往往整定不良、效果欠佳,对运行工况的适应能力很差。

    二、PID控制器各校正环节

       任何闭环控制系统的首要任务是要稳(稳定)、快(快速)、准(准确)的响应命令。PID调整的主要工作就是如何实现这一任务。

      增大比例系数P将加快系统的响应,它的作用于输出值较快,但不能很好稳定在一个理想的数值,不良的结果是虽较能有效的克服扰动的影响,但有余差出现,过大的比例系数会使系统有比较大的超调,并产生振荡,使稳定性变坏。积分能在比例的基础上消除余差,它能对稳定后有累积误差的系统进行误差修整,减小稳态误差。微分具有超前作用,对于具有容量滞后的控制通道,引入微分参与控制,在微分项设置得当的情况下,对于提高系统的动态性能指标,有着显著效果,它可以使系统超调量减小,稳定性增加,动态误差减小。

       综上所述,P—比例控制系统的响应快速性,快速作用于输出,好比"现在"(现在就起作用,快),I—积分控制系统的准确性,消除过去的累积误差,好比"过去"(清除过去积怨,回到准确轨道),D—微分控制系统的稳定性,具有超前控制作用,好比"未来"(放眼未来,未雨绸缪,稳定才能发展)。当然这个结论也不可一概而论,只是想让初学者更加快速的理解PID的作用。

      在调整的时候,你所要做的任务就是在系统结构允许的情况下,在这三个参数之间权衡调整,达到最佳控制效果,实现稳快准的控制特点。

       比例控制可快速、及时、按比例调节偏差,提高控制灵敏度,但有静差,控制精度低。积分控制能消除偏差,提高控制精度、改善稳态性能,但易引起震荡,造成超调。微分控制是一种超前控制,能调节系统速度、减小超调量、提高稳定性,但其时间常数过大会引入干扰、系统冲击大,过小则调节周期长、效果不显著。比例、积分、微分控制相互配合,合理选择PID调节器的参数,即比例系数KP、积分时间常数τi和微分时间常数τD,可迅速、准确、平稳的消除偏差,达到良好的控制效果。

      1. 比例环节

       成比例地反映控制系统的偏差信号e(t),偏差一旦产生,控制器立即产生控制作用,以减小偏差。当仅有比例控制时系统输出存在稳态误差(Steady-state error)。

       P参数越大比例作用越强,动态响应越快,消除误差的能力越强。但实际系统是有惯性的,控制输出变化后,实际y(t)值变化还需等待一段时间才会缓慢变化。由于实际系统是有惯性的,比例作用不宜太强,比例作用太强会引起系统振荡不稳定。P参数的大小应在以上定量计算的基础上根据系统响应情况,现场调试决定,通常将P参数由大向小调,以能达到最快响应又无超调(或无大的超调)为最佳参数。

      优点:调整系统的开环比例系数,提高系统的稳态精度,减低系统的惰性,加快响应速度。

      缺点:仅用P控制器,过大的开环比例系数不仅会使系统的超调量增大,而且会使系统稳定裕度变小,甚至不稳定。
      
      2. 积分环节

       控制器的输出与输入误差信号的积分成正比关系。主要用于消除静差,提高系统的无差度。积分作用的强弱取决于积分时间常数T,T越大,积分作用越弱,反之则越强。

      为什么要引进积分作用?

       比例作用的输出与误差的大小成正比,误差越大,输出越大,误差越小,输出越小,误差为零,输出为零。由于没有误差时输出为零,因此比例调节不可能完全消除误差,不可能使被控的PV值达到给定值。必须存在一个稳定的误差,以维持一个稳定的输出,才能使系统的PV值保持稳定。这就是通常所说的比例作用是有差调节,是有静差的,加强比例作用只能减少静差,不能消除静差(静差:即静态误差,也称稳态误差)。

       为了消除静差必须引入积分作用,积分作用可以消除静差,以使被控的y(t)值最后与给定值一致。引进积分作用的目的也就是为了消除静差,使y(t)值达到给定值,并保持一致。

       积分作用消除静差的原理是,只要有误差存在,就对误差进行积分,使输出继续增大或减小,一直到误差为零,积分停止,输出不再变化,系统的PV值保持稳定,y(t)值等于u(t)值,达到无差调节的效果。

       但由于实际系统是有惯性的,输出变化后,y(t)值不会马上变化,须等待一段时间才缓慢变化,因此积分的快慢必须与实际系统的惯性相匹配,惯性大、积分作用就应该弱,积分时间I就应该大些,反之而然。如果积分作用太强,积分输出变化过快,就会引起积分过头的现象,产生积分超调和振荡。通常I参数也是由大往小调,即积分作用由小往大调,观察系统响应以能达到快速消除误差,达到给定值,又不引起振荡为准。

       对一个自动控制系统,如果在进入稳态后存在稳态误差,则称这个控制系统是有稳态误差的或简称有差系统(System with Steady-state Error)。为了消除稳态误差,在控制器中必须引入“积分项”。积分项对误差取决于时间的积分,随着时间的增加,积分项会增大。这样,即便误差很小,积分项也会随着时间的增加而加大,它推动控制器的输出增大使稳态误差进一步减小,直到等于零。因此,比例+积分(PI)控制器,可以使系统在进入稳态后无稳态误差。PI控制器不但保持了积分控制器消除稳态误差的“记忆功能”,而且克服了单独使用积分控制消除误差时反应不灵敏的缺点。

      优点:消除稳态误差。
      
      缺点:积分控制器的加入会影响系统的稳定性,使系统的稳定裕度减小。

      3. 微分环节

       反映偏差信号的变化趋势,并能在偏差信号变得太大之前,在系统中引入一个有效的早期修正信号,从而加快系统的动作速度,减少调节时间。在微分控制中,控制器的输出与输入误差信号的微分(即误差的变化率)成正比关系。

      为什么要引进微分作用?

       前面已经分析过,不论比例调节作用,还是积分调节作用都是建立在产生误差后才进行调节以消除误差,都是事后调节,因此这种调节对稳态来说是无差的,对动态来说肯定是有差的,因为对于负载变化或给定值变化所产生的扰动,必须等待产生误差以后,然后再来慢慢调节予以消除。

       但一般的控制系统,不仅对稳定控制有要求,而且对动态指标也有要求,通常都要求负载变化或给定调整等引起扰动后,恢复到稳态的速度要快,因此光有比例和积分调节作用还不能完全满足要求,必须引入微分作用。比例作用和积分作用是事后调节(即发生误差后才进行调节),而微分作用则是事前预防控制,即一发现y(t)有变大或变小的趋势,马上就输出一个阻止其变化的控制信号,以防止出现过冲或超调等。
    D越大,微分作用越强,D越小,微分作用越弱。系统调试时通常把D从小往大调,具体参数由试验决定。

       如:由于给定值调整或负载扰动引起y(t)变化,比例作用和微分作用一定等到y(t)值变化后才进行调节,并且误差小时,产生的比例和积分调节作用也小,纠正误差的能力也小,误差大时,产生的比例和积分作用才增大。因为是事后调节动态指标不会很理想。而微分作用可以在产生误差之前一发现有产生误差的趋势就开始调节,是提前控制,所以及时性更好,可以最大限度地减少动态误差,使整体效果更好。但微分作用只能作为比例和积分控制的一种补充,不能起主导作用,微分作用不能太强,太强也会引起系统不稳定,产生振荡,微分作用只能在P和I调好后再由小往大调,一点一点试着加上去。

       自动控制系统在克服误差的调节过程中可能会出现振荡甚至失稳。其原因是由于存在有较大惯性组件(环节)或有滞后(delay)组件,具有抑制误差的作用,其变化总是落后于误差的变化。解决的办法是使抑制误差的作用的变化“超前”,即在误差接近零时,抑制误差的作用就应该是零。这就是说,在控制器中仅引入“比例”项往往是不够的,比例项的作用仅是放大误差的幅值,而目前需要增加的是“微分项”,它能预测误差变化的趋势。这样,具有比例+微分的控制器,就能够提前使抑制误差的控制作用等于零,甚至为负值,从而避免了被控量的严重超调。所以对有较大惯性或滞后的被控对象,比例+微分(PD)控制器能改善系统在调节过程中的动态特性。PD控制只在动态过程中才起作用,对恒定稳态情况起阻断作用。因此,微分控制在任何情况下都不能单独使用。

      优点:使系统的响应速度变快,超调减小,振荡减轻,对动态过程有“预测”作用。

       在低频段,主要是PI控制规律起作用,提高系统型别,消除或减少稳态误差;在中高频段主要是PD规律起作用,增大截止频率和相角裕度,提高响应速度。因此,控制器可以全面地提高系统的控制性能。

    三、PID控制器的参数整定

       PID控制器的参数整定是控制系统设计的核心内容。它是根据被控过程的特性确定PID控制器的比例系数、积分时间和微分时间的大小。PID控制器参数整定的方法很多,概括起来有两大类:

      1. 理论计算整定法

       它主要是依据系统的数学模型,经过理论计算确定控制器参数。这种方法所得到的计算数据未必可以直接用,还必须通过工程实际进行调整和修改。

      2. 工程整定方法

       它主要依赖工程经验,直接在控制系统的试验中进行,且方法简单、易于掌握,在工程实际中被广泛采用。PID控制器参数的工程整定方法,主要有临界比例法、反应曲线法和衰减法。三种方法各有其特点,其共同点都是通过试验,然后按照工程经验公式对控制器参数进行整定。但无论采用哪一种方法所得到的控制器参数,都需要在实际运行中进行最后调整与完善。现在一般采用的是临界比例法。利用该方法进行 PID控制器参数的整定步骤如下:

      (1)首先预选择一个足够短的采样周期让系统工作;

      (2)仅加入比例控制环节,直到系统对输入的阶跃响应出现临界振荡,记下这时的比例放大系数和临界振荡周期;

      (3)在一定的控制度下通过公式计算得到PID控制器的参数。

      PID调试一般原则

      a.在输出不振荡时,增大比例增益P。
      b.在输出不振荡时,减小积分时间常数Ti。
      c.在输出不振荡时,增大微分时间常数Td。

      PID调试一般步骤

      a. 确定比例增益P

      确定比例增益P 时,首先去掉PID的积分项和微分项,一般是令Ti=0、Td=0(具体见PID的参数设定说明),使PID为纯比例调节。输入设定为系统允许的最大值的60%~70%,由0逐渐加大比例增益P,直至系统出现振荡;再反过来,从此时的比例增益P逐渐减小,直至系统振荡消失,记录此时的比例增益P,设定PID的比例增益P为当前值的60%~70%。比例增益P调试完成。

      b. 确定积分时间常数Ti

      比例增益P确定后,设定一个较大的积分时间常数Ti的初值,然后逐渐减小Ti,直至系统出现振荡,之后在反过来,逐渐加大Ti,直至系统振荡消失。记录此时的Ti,设定PID的积分时间常数Ti为当前值的150%~180%。积分时间常数Ti调试完成。

      c. 确定微分时间常数Td

      微分时间常数Td一般不用设定,为0即可。若要设定,与确定 P和Ti的方法相同,取不振荡时的30%。

      d. 系统空载、带载联调,再对PID参数进行微调,直至满足要求。

       变速积分的基本思想是,设法改变积分项的累加速度,使其与偏差大小相对应:偏差越大,积分越慢;反之则越快,有利于提高系统品质。

    转载的地址http://blog.sciencenet.cn/blog-699887-948853.html

    大家再看看维基百科上面的PID的动图。

     

    https://zh.wikipedia.org/wiki/PID%E6%8E%A7%E5%88%B6%E5%99%A8

    维基百科上面讲的也比较清楚,结合起来看挺好。

    多谢小伙伴更正了里面的小错误,步骤C为微分时间@lubingabby

    展开全文
  • 一套鼠标键盘控制多台电脑,无界鼠标 (Mouse without Borders)控制界面的功能翻译: 易君对英语一窍不通 今天在网上找了很久 无界鼠标的使用安装教程倒是很 但没有完整的功能界面翻译 花了点时间终于把它翻译...

    一套鼠标键盘控制多台电脑,无界鼠标 (Mouse without Borders)控制界面的功能翻译:

    易君对英语一窍不通
    今天在网上找了很久
    无界鼠标的使用安装教程倒是很多
    但没有完整的功能界面翻译
    花了点时间终于把它翻译了。
    在这里插入图片描述

    翻译

    machine Setup:机器安装
    disable easy mouse:选择后当前机器的鼠标无法向其他机器自由移动
    share chipboard:共享剪切板
    Hide logo form Logon Screen:隐藏标志形式登录屏幕
    Hide mouse at Screen edge:将鼠标隐藏在屏幕边缘
    Draw mouse cursor:绘制鼠标光标
    Validate remote machine IP Address:验证远程机器ip地址
    same subnet only:只有同一个子网
    disable CAD:禁止 (ctrl+alt+del)功能
    block screen saver on other machines:当鼠标移动到目标机器后,禁止目标机器的屏幕保护程序
    move mouse relativly:相对地移动鼠标
    block mouse at screen corners:在屏幕角阻挡鼠标
    use key mappings:使用金钥映射
    show clipboard/network status message:显示剪贴板/网络状态消息
    Send error log:发送错误日志

    快捷键设置部分:
    switch between machines:设置快速切换控制机器快捷键
    show settings form:显示或隐藏设置界面
    lock the controlled machine:锁定当前控制的机器
    reconnect to other machines:重新连接
    Custom screen capture:自定义屏幕捕捉
    exit the application:退出当前应用
    switch to all pc mode:切换到所有PC控制方式(这种方式下鼠标或键盘的操作在所有联通的机器中都有效)
    Easy mouse:轻松鼠标
    Toggle easy mouse:切换简单鼠标

    说明

    Mouse without Borders (无界鼠标)是微软推出的免费工具,它也可以让你实现使用一套键盘与鼠标同时控制多台不同的电脑,让你的鼠标指针可以在它们不同的屏幕之间穿梭,非常的方便实用!同时解决了Synergy的不足之处,只能在不同电脑间拷贝粘贴文本,不能拷贝文件,而Mouse without Borders就解决了这个问题,无界鼠标最多可以把四台PC电脑连接在一起,用户可以用一个鼠标在不同电脑上完成复制、粘贴、拖拽等操作,感觉就像在用同一台电脑一样。除了支持文件操作之外,“无界鼠标”还可以让用户通过该软件在一台电脑上登录到所有其他相连接的电脑上。此外,你还可以通过该软件自定义桌面壁纸,图片来自Bing的每日一图。当然,这一切的条件前提是你的电脑都是处于同一局域网下。

    无界鼠标由于是微软出品的,所以只支持 Windows 平台,安装包也是 MSI 文件格式。安装和配置上比 Synergy相对简单,可配置的项目不算多,最大的优点是可以实现跨电脑拷贝文件与拖放文件。另外,无界鼠标的电脑们是完全对等模式,你拿起任意一台电脑连接的鼠标和键盘都能操作其它电脑,而 Synergy 只能由“服务器”的电脑上键鼠控制其他电脑,而其他电脑的指针只能被困在自己的屏幕里面。

    Mouse without Borders有以下主要功能:

    1. Windows平台下多机器间共享一套鼠标和键盘,可提高使用KVM的效率
    2. 容易从一台机器复制和黏贴文本或图像到其它所有机器
    3. 能够在机器间拖拽文件
    4. 能够对其它机器截屏
    5. 能够切换成同步控制所有机器
    6. 可定制Windows7的登录屏幕
    7. 完全对等模式,所有电脑间都是对等的,可以拿起任意一台电脑连接的鼠标和键盘操作其它电脑

    官方地址:
    https://www.microsoft.com/en-us/download/details.aspx?id=35460

    感谢你的查阅,希望可以帮到你,祝你学习愉快!
    我是和你一起学习的 易君

    展开全文
  • 微信小程序调用蓝牙功能控制车位锁

    千次阅读 热门讨论 2018-05-02 16:30:16
    第一次学用微信小程序,项目需要,被逼着研究了一下,功能是调用微信小程序的蓝牙功能,连接上智能车位锁,控制升降,大概步骤及调用的小程序接口API如下: 1。打开蓝牙模块 wx.openBluetoothAdapter(OBJECT) 2。...
  • 解决方法: 服务器上: 打开服务器管理器——仪表板——快速启动——2添加角色和功能——3个下一步——然后选择windows server更新...控制面板------程序--------启用和关闭Windows功能-----勾选Frameword 4.0 ...
  • ARM多功能脚配置

    千次阅读 2013-11-13 00:48:46
    多功能脚配置,包括配置多功能脚的功能,上拉,下拉,不带上下拉还是保持,驱动电流, arm的多功能脚可以配置为gpio功能,功能号为0,可以配置输入或者输出功能由输出使能位决定,为1就是输出,为0就是输入; 也可以...
  • 计算机组成原理-实验三-多功能ALU设计实验

    万次阅读 多人点赞 2018-06-04 21:29:50
    本实验要求设计一个具有8种运算功能的32位ALU,并能够产生运算结果的表示:结果为零标志ZF(Zero Flag)和溢出...ALU通过4根控制线ALU_OP[3:0]来选择其8种功能功能见表所示,多余的8位用于后继运算的拓展备用。 ...
  • 按钮切换全屏和退出全屏功能 进度条和时间 前期准备 HTML DOM Video 对象文档 图标字体库下载和文档 案例目录 HTML代码 我们自定义实现控制条,所以video不要使用controls属性 从其他网站视频播放,...
  • SAP系统中信用控制功能详解

    万次阅读 多人点赞 2017-02-17 14:25:39
    1、引言 现金销售和预收款销售一般指发生在垄断性行业,多数企业不得不面对产品赊销的两难选择,赊销是把双刃剑,如果不赊销,不...处理这个问题的关键就在于如何处理应收账款管理、进行信用管理和风险控制防范,在
  • 本文主要讲述WinForm开发应用程序需要设置自启动功能,这个也是在实际开发中经常涉及到的,非常实用,所讲到的是通过注册表来控制程序是否自行启动,具体功能实现上两张图,更直观。 如下图:程序设置保持界面实现...
  • java实现打印功能控制打印方向

    千次阅读 2015-08-05 12:38:31
    定义打印机,限制那些返回到提供你要实现功能的函数的列表。打印服务实现了PrintService接口。2.通过调用接口中定义的createPrintJob()方法创建一个打印事件,作为DocPrintJob的一个实例。3.创建一个实现Doc接口的类...
  • DLT645-2007 多功能电表通讯

    千次阅读 2017-02-11 11:45:19
    DLT645-2007 多功能电表通讯
  • Windows安装MySql服务无法响应控制功能

    万次阅读 多人点赞 2019-08-22 23:55:12
    服务没有响应控制功能。 请键入 NET HELPMSG 2186 以获得更的帮助。 ############################### 出现这个问题很无语,以为安装错了或者目录设置错了,或者my.ini少了\\,或者初始化错了,最后搞了...
  • 计算机组成原理18----控制单元CU功能

    千次阅读 2019-07-10 09:25:13
    控制单元CU功能 1.微操作命令分析 1.1取指周期 1.2间址周期 1.3执行周期 1.4中断周期 2.控制单元功能 2.1外特性 2.2控制信号 2.3多级时序系统 2.4控制方式 CPU结构如下所示,主要由ALU,CU,寄存器和...
  • 1、针对VCU功能,应至少满足如下测试需求: 系统上下电测试; 能量管理功能测试; 扭矩分配功能测试; 手动驾驶模拟功能测试; 自定义驾驶循环工况测试; 与其他 ECU 的协控功能测试; 系统诊断功能测试; ...
  • 飞塔防火墙除了保证内网不受攻击之外,也具备上网行为管理功能,例如限定某人是否可以上网,上网流量有多少,也可以限制哪些软件不能用,哪些网站不能打开等,对应用软件的管控,就需要用到防火墙的应用控制功能
  • 基于51单片机—多功能秒表

    千次阅读 多人点赞 2018-07-10 13:07:42
    1.两片573驱动三个数码管显示 2.显示范围:00.0~99.9 3.一次计时中可储4个数据在EEPROM中 4.两个按键控制,暂停,启动,存数,翻页
  • 首先要说,Teamviewer确实是极其优秀的远程软件。Mac下远程协助工具并不容易做,你看我们等了这么久,Mac QQ 才更新到了屏幕共享功能,仍然不支持远程协助。远方...
  • 简介:这里我们使用了一片SN3112来控制led灯的亮灭,SN3112是一片12路led控制芯片。每路单独256级亮度可控,CPU通过I2C接口对其进行编程控制。通过将SDB脚拉低或者软件设置可关断芯片,进入低功耗模式。  SN3112...
  • 在很应用行业里面,都对数据的权限做了特别的声明,如对于销售,财务的数据,它们是非常敏感的,因此要求对数据权限进行控制,对于基于集团性的应用系统而言,就更需要控制好各自公司的数据了。如默认只能看本...
  • 支持串口硬件流控制"; (6).支持实时保存调试记录。 (7).自动记录用户使用设置习惯。 (8).首次运行后,程序将入驻系统右键菜单栏。  一、软件目录 二、运行软件(支持中文数据收发) 三...
  • 拥有家电控制能力是优势。  随着人工智能时代的来临,人们的家庭生活也越来越趋向于简单化、自动化。但是一个家庭一旦打算购入智能的机器人作为小助手或管家辅助使用,就会萌生这样一个问题:要买哪个牌子具备...
  • 基于Spring cloud ribbon实现版本控制

    千次阅读 2018-01-23 10:26:54
    在我们使用spring mvc单体架构时, 我们可以通过uri,或者请求头做版本路由,虽然同一个功能需要维护个版本的接口,但是对于系统而言,不会因为新增一个接口版本而影响到老用户。当我们使用spring cloud构建微...
  • 基于单片机的多功能电子密码锁系统设计

    千次阅读 多人点赞 2018-11-29 22:38:09
    该密码锁是以STC89C52单片机为核心,加上需要的硬件部分,实现密码的设定、保存、检测,从而控制电路的闭合是否进行开锁的决定。单片机通过用户输入的密码,转化成键码,与保存在24C02芯片中的密码进行比较,本次...
  • 前面两个专题分别介绍了最近的两个比较热门的智能家居爆品,现在,小智接下来要分享的是小米旗下的米家品牌智能产品...下面,小智先给大家介绍一下米家的这个多功能网关。一、基本参数(转自小米官网)二、应用场景...
  • AGV控制系统搭建

    万次阅读 多人点赞 2019-06-25 15:32:33
     本文介绍自动导引车(AGV)车载控制系统的实现过程,分为硬件搭建和软件设计两部分,并在其中穿插 AGV 控制的基础知识讲解。 1. 车载控制器 1.1 控制器的类型  车载控制器是控制系统乃至整个 AGV 的...
  • 最近在一个项目中,需要制作录屏的功能,原先是在应用中有录屏/控制的按钮,思考之下觉得这种效果并不好,因此就想制作一个可以悬浮的悬浮窗,这样不论手机在什么界面中都可以对录屏功能进行控制。 这里就来构建一...
  • Oracle R12 组织访问的控制 - MOAC(Multi-Org Access Control)MOAC(Multi-Org Access Control)为组织访问控制,是Oracle EBS R12的重要新功能,它可以实现在一个Responsibility下对个Operation Unit(OU)进行...
  • 1YAP终端机简介YAP物联网通信终端基于合宙当红物联网Cat.1模块—— Air724UG,采用蜂窝物联网4G LTE Cat.1通信技术,兼具通讯和边缘数据处理功能,可以胜任多数物联网应用场景的技术学习和产品原型开发场景。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,122,800
精华内容 849,120
关键字:

多功能控制