团队介绍 首先贴出卧龙凤雏团队与电磁炮的合影
最中间是总策划、酷爱军事、从小就造飞机的“曹工”,主要负责坦克车体外观及机械结构及PCB电路板的设计;左边的是我,负责机器人顶层控制逻辑中的视觉识别与追踪;右边是负责车体底层控制的“陈工”。
制作过程 外形设计 底盘设计 机器人底盘长788.45mm,宽308.00mm,高153.51mm,悬挂采用的是克里斯蒂悬挂系统,即一种拥有大直径负重轮,使用螺旋弹簧的独立式悬挂装置。这种悬挂均是由前后两个互相连接的圆柱形螺旋弹簧构成。位于前方的为可调式水平螺旋弹簧,后方的则是垂直螺旋弹簧,这种设计有更长的避震行程,可强化越野性能。
机器人底盘侧视图
“曹工”组装车体
组装完成
磁加速系统设计
磁加速系统实际模型
炮台底座也使用木制材料,中间开有41mm*174mm的矩形孔为炮位提供移动的空间,整个炮塔通过螺栓固定在履带底盘上部的旋转滑台上。磁加速系统的加速段整体用木板封装起来以此来达到绝缘的效果;为了防止热量堆积在炮管支架的木板上每一级加速线圈周围都开有方形空用来散热;
磁加速系统炮管
电磁加速系统为磁阻式电磁加速系统,采用五级线圈加速,光电门进行击发,电解电容储能,发射电压为400V。第一级电容容量为2000μF,第二级电容容量为1880μF,第三级电容容量为1880μF,第四级电容容量为1440μF,第五级电容容量为1440μF,这样的电容容量设计是为了减少炮弹在加速的过程中所受到的反拉
磁加速系统PCB
电容储能系统实物
该磁加速系统配备有自动装弹机,装弹机安装在炮管的尾部并随炮管运动可以实现任意角度装填,装填机构通过丝杆推动炮弹并将其送入炮膛,丝杆由四线两相步进电机提供动力,在装弹机滑台的两侧安装有两个限位开关来限制推弹杆的移动空间。
自动装弹机示意图
经过建立数学模型进行计算并通过测试,该磁加速系统可以将我们所设计的炮弹加速至45m/s左右。
炮弹实物
对炮弹的空气动力模拟计算图示
电路设计
遥控器原理图
遥控器PCB
底层控制板原理图
底层控制板PCB
上路测试 开着出去,抬着回来
软件部分 由于后来想要拿此机器人参加一些比赛,所以要为电磁炮想出应用一些民用价值。考虑到现在楼层建筑越来越高,若发生火灾消防部队扑救的难度很大,如果电磁炮可以对火源发出灭火弹则可迅速阻止火势扩大,因此就以“基于磁加速系统的高层建筑灭火机器人”为背景继续完善机器人。
初步方案是用树莓派进行火焰识别,并用舵机云台进行火焰追踪,将角度数据回传给底层STM32控制端后进行炮塔对火焰的追踪,最后再根据利用炮口的激光测距仪测出的炮口与火焰的距离进行炮弹射速的调整。
实现原理图
初步实现效果演示
优化改进 由于树莓派识别火焰的速率实在不敢恭维,于是家中有矿的”曹工“斥一顿早饭钱买了一块英伟达Jetson Nano边缘计算模块,Jetson nano与树莓派相比,NVIDIA Jetson Nano包含性能更高,功能更强大的GPU——NVIDIA Jetson Nano中具有128个CUDA核心的NVIDIA Maxwell GPU。NVIDIA Jetson Nano中更强大的GPU可以为图形处理,甚至人工智能(AI)和机器学习(ML)提供更强大的功能。
Jetson Nano
火焰识别算法采用的是NanoDet目标检测模型,NanoDet 是一个速度超快和轻量级的移动端目标检测模型,非常适合嵌入式部署。将在PC端训练好的模型移植到Jetson Nano边缘计算平台上即可进行火焰识别。识别到火焰后,根据火焰在视角中的位置,摄像头云台利用PID算法进行追踪。Jetson Nano通过PCA9655 舵机驱动板驱动摄像头云台舵机。
PID算法代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 # *****************************************************************# # 增量式PID系统 # # *****************************************************************# class IncrementalPID: def __init__(self, P, I, D): self.Kp = P self.Ki = I self.Kd = D self.PIDOutput = 0.0 # PID控制器输出 self.SystemOutput = 0.0 # 系统输出值 self.LastSystemOutput = 0.0 # 上次系统输出值 self.Error = 0.0 # 输出值与输入值的偏差 self.LastError = 0.0 self.LastLastError = 0.0 # 设置PID控制器参数 def SetStepSignal(self, StepSignal): self.Error = StepSignal - self.SystemOutput IncrementValue = self.Kp * (self.Error - self.LastError) + \ self.Ki * self.Error + \ self.Kd * (self.Error - 2 * self.LastError + self.LastLastError) self.PIDOutput += IncrementValue self.LastLastError = self.LastError self.LastError = self.Error # 设置一阶惯性环节系统 其中InertiaTime为惯性时间常数 def SetInertiaTime(self, InertiaTime, SampleTime): self.SystemOutput = (InertiaTime * self.LastSystemOutput + \ SampleTime * self.PIDOutput) / (SampleTime + InertiaTime) self.LastSystemOutput = self.SystemOutput
火焰追踪主函数代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 if __name__ == '__main__': pwm = PCA9685(0x40, debug=True) pwm.setPWMFreq(50) pwm.setServoPulse(0, 1500) pwm.setServoPulse(1, 1500) torch.backends.cudnn.enabled = True torch.backends.cudnn.benchmark = True load_config(cfg, config_path) logger = Logger(-1, use_tensorboard=False) predictor = Predictor(cfg, model_path, logger, device='cuda:0') logger.log('Press "Esc" to exit.') camera = USBCamera(width=320, height=240, capture_fps=30) camera.running = True while True: frame = camera.value # ret_val, frame = cap.read() # if ret_val == False: # continue #skip if capture fail meta, res = predictor.inference(frame) predictor.visualize(res, meta, cfg.class_names, 0.35) print('x,y', x, y) if (x != 0 and y != 0): # 输入X轴方向参数PID控制输入 xservo_pid.SystemOutput = x xservo_pid.SetStepSignal(160) xservo_pid.SetInertiaTime(0.01, 0.1) target_valuex = int(1500 + xservo_pid.SystemOutput) # 输入Y轴方向参数PID控制输入 yservo_pid.SystemOutput = y yservo_pid.SetStepSignal(120) yservo_pid.SetInertiaTime(0.01, 0.1) print('yservo_pid.SystemOutput', yservo_pid.SystemOutput) target_valuey = int(1500 - yservo_pid.SystemOutput) if target_valuey < 820: target_valuey = 820 if target_valuey > 2500: target_valuey = 2500 if target_valuey > 2500: target_valuex = 2500 if target_valuex < 10: target_valuex = 10 pwm.setServoPulse(0, target_valuex) pwm.setServoPulse(1, target_valuey) print('valuex,valuey', target_valuex, target_valuey)
火焰追踪效果如下