故障诊断与处理聊的是出了问题怎么应对。这篇换个角度——怎么从设计层面让系统尽量少出问题。可靠性设计是"防患于未然",故障处理是"亡羊补牢",两者配合才能真正做出高可用的机器人产品。

很多团队做原型的时候不考虑可靠性,觉得能跑就行。等上了产线、进了客户现场,三天两头宕机,才回头补课。可靠性不是加上去的,是设计出来的。从架构设计的第一天就要把可靠性考虑进去。

面试问到系统设计,如果你能主动提到可靠性方面的考量——冗余设计、容错机制、状态管理——面试官会认为你有产品思维,不只是个写算法的。

一、冗余设计

冗余是提升可靠性最直接的手段。关键部件要有备份,一个挂了另一个能顶上。

硬件冗余:双激光雷达配置、双IMU、双计算单元。工业级机器人通常会在关键传感器上做冗余。成本增加了,但可用性也提高了。实际项目中要根据成本约束做取舍——不是所有部件都值得做冗余,关键路径上的部件才需要。比如导航机器人的前向激光雷达必须有备份,但顶部的安全激光雷达可以不做。

软件冗余:关键算法跑两套,对比输出结果。如果两套算法结果差异太大,说明有一套可能出了问题,切换到安全模式。这种"双模冗余"在航空航天领域用得很多,机器人领域也开始采用。

# 双模冗余示例
class RedundantPlanner:
    def plan(self, start, goal, costmap):
        path_a = self.planner_a.plan(start, goal, costmap)
        path_b = self.planner_b.plan(start, goal, costmap)
        if paths_similar(path_a, path_b):
            return path_a
        # 两套算法结果不一致,进入安全模式
        return self.fallback_planner.plan(start, goal, costmap)

通信冗余:双网口、双通道通信。主通道断了自动切到备用通道。ROS2的DDS本身支持多网卡,配置好就行。

时间冗余:关键操作执行多次确认。比如导航到达目标点后,再做一次定位确认,确保真的到了。机械臂抓取后检查夹爪力反馈,确认抓稳了再移动。

二、容错架构

容错设计的核心思想是:一个模块出错不应该导致整个系统崩溃。

进程隔离:每个关键模块运行在独立进程里。一个进程崩溃不影响其他进程。ROS2的组件容器(Component Container)支持把多个组件放在同一个进程里,也支持每个组件独立进程运行。关键模块推荐用独立进程。

# 进程监控与自动重启
import subprocess
import time

def watch_process(cmd, name):
    while True:
        proc = subprocess.Popen(cmd)
        proc.wait()
        if proc.returncode != 0:
            log_error(f'{name} crashed, restarting...')
        time.sleep(1)  # 避免频繁重启

超时保护:每个模块的处理都要有超时限制。一个回调执行超过预期时间,要强制中断或者报警。不能让一个慢模块拖垮整个系统。ROS2的callback group可以设置超时时间。

状态一致性:分布式系统里最头疼的问题之一。多个模块各自维护状态,如果一个模块重启了,它的状态和其他模块不一致了怎么办?解决方案是设计状态同步机制——重启后从其他模块或者持久化存储里恢复状态。关键状态要定期快照,恢复时加载最近的快照就行。

# 状态持久化与恢复
class StateManager:
    def save_state(self, state):
        with open('/tmp/robot_state.json', 'w') as f:
            json.dump(state, f)
    
    def load_state(self):
        try:
            with open('/tmp/robot_state.json', 'r') as f:
                return json.load(f)
        except FileNotFoundError:
            return self.default_state()

三、防御性编程

可靠性设计在代码层面的体现就是防御性编程。不信任任何外部输入,对所有输入做校验。

传感器数据要做范围检查、类型检查、时间戳检查。网络消息要做格式校验、长度限制、签名验证。用户输入要做白名单过滤。这些检查看起来是"多余的代码",但在生产环境里能挡住大量异常。

def process_lidar(data):
    if data is None or len(data.points) == 0:
        return None
    if not is_valid_timestamp(data.header.stamp, max_delay=0.5):
        return None
    # 过滤掉明显异常的点
    valid_points = [p for p in data.points 
                    if abs(p.z) < 10.0 and not math.isnan(p.x)]
    return valid_points

内存安全也是可靠性的重要一环。C++里的内存泄漏、野指针、缓冲区溢出,都是导致系统不稳定的常见原因。推荐用智能指针、用RAII管理资源、用AddressSanitizer做定期检测。

还有一个容易被忽视的可靠性杀手:未处理的异常。Python里的try-except要覆盖所有可能抛出异常的代码,特别是网络通信、文件操作、第三方库调用。一个未捕获的异常就能让整个进程崩溃。C++里则是段信号和异常处理要设计好,不能默认忽略SIGPIPE这类信号。

资源限制也很重要。给每个进程设置内存上限(用cgroup或者ulimit),防止某个进程内存泄漏把整个系统拖垮。给日志目录设置大小限制,防止磁盘被写满。这些"基础设施"层面的保护措施往往比代码层面的防御更有效。

四、可靠性测试

设计得再好,不测试也不知道靠不靠谱。可靠性测试要模拟各种异常场景。

长时间运行测试(烤机测试):让系统连续运行72小时甚至更久,观察内存是否持续增长、性能是否逐渐下降、有没有偶发崩溃。这种测试最枯燥但最有价值。很多内存泄漏和时序问题只有在长时间运行后才会暴露。建议搭一个自动化烤机环境,系统自动跑各种任务,跑完自动出报告。

故障注入测试:主动制造故障,看系统怎么应对。随机杀掉某个进程、断开某个传感器、把网络延迟调到很高。看系统能不能自动恢复、会不会数据丢失、能不能安全降级。

# 故障注入示例:随机杀掉进程
while true; do
    sleep $((RANDOM % 60 + 30))
    kill -9 $(pgrep -f "navigation_node")
done

边界条件测试:把机器人放在极端环境里测试。地图特别大、障碍物特别密、网络特别差、传感器数据有噪声。这些边界条件在正常使用中不常遇到,但一旦遇到系统不能崩。

五、面试高频追问

Q:你怎么评估系统的可靠性? A:用MTBF(平均无故障时间)和MTTR(平均恢复时间)两个指标。MTBF越长越好,MTTR越短越好。通过长时间运行测试统计MTBF,通过故障注入测试统计MTTR。工业级机器人一般要求MTBF大于1000小时。

Q:怎么防止内存泄漏? A:C++用智能指针(shared_ptr/unique_ptr)管理内存,用RAII管理资源。定期用valgrind和AddressSanitizer检测。Python要注意循环引用和未关闭的文件句柄。最关键的是代码review的时候就要关注资源管理。

Q:单点故障怎么处理? A:识别系统中的单点故障,然后逐个消除。关键传感器做冗余、关键计算节点做主备、关键通信链路做双通道。如果成本不允许做硬件冗余,至少在软件层面做好故障检测和快速恢复。

系统可靠性设计是机器人从demo到产品的关键跨越。冗余设计、容错架构、防御性编程、可靠性测试,这四个方面做好了,系统的可用性才能有保证。可靠性设计不是一次性的工作,要持续迭代改进。

上一篇:第281篇 故障诊断与处理 

下一篇我们聊边缘计算部署。


系统可靠性设计是机器人产品化的基石。冗余设计、容错架构、防御性编程、可靠性测试,这四个维度构成了完整的可靠性工程体系。下一篇聊边缘计算部署。

如果这篇文章对你有帮助,欢迎点赞支持一下,你的鼓励是我持续更新的动力!

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐