Pybullet+ROS:融合仿真与控制,开辟机器人新时代
发布时间: 2024-12-24 16:23:38 阅读量: 9 订阅数: 10
基于ROS的点焊机器人仿真与控制项目源码.zip
5星 · 资源好评率100%
![pybullet手册英文版pdf资料](https://tayalmanan28.github.io/my_blogs/images/pybullet.png)
# 摘要
本文系统介绍了Pybullet与ROS在机器人技术领域的应用,首先对二者进行了概述,并探讨了Pybullet仿真环境的搭建方法,包括基础理论、仿真环境配置以及机器人模型导入技术。接着,深入分析了ROS机器人控制的基础知识,包括系统架构、消息传递机制以及基本控制和消息服务的应用。进一步地,本文阐述了Pybullet与ROS的集成方法和在仿真应用中的实现,特别是在实际控制结合中的策略测试与优化。文章还探讨了Pybullet+ROS在机器人自主导航、路径规划和复杂环境操作中的创新应用,并以实践项目案例的方式分析了问题解决策略和对未来实践的建议。本文为Pybullet与ROS的集成应用提供了全面的理论支撑和实践指导,对于机器人技术的发展与创新应用具有重要的参考价值。
# 关键字
Pybullet;ROS;仿真环境;机器人控制;集成应用;自主导航
参考资源链接:[PyBullet官方指南:从入门到高级控制](https://wenku.csdn.net/doc/1q4393nht9?spm=1055.2635.3001.10343)
# 1. Pybullet与ROS概述
## 1.1 Pybullet简介
Pybullet是一个用于机器人仿真、游戏开发和机器学习的模块化、轻量级软件包,它是Bullet Physics SDK的Python接口。Pybullet提供了一个丰富的API,允许用户设计、模拟并交互虚拟机器人环境。其广泛应用于研究领域,特别是在机器学习和动力学研究方面,因为其可以快速模拟复杂的物理交互。Pybullet相较于其同门师兄弟,如V-REP或者Gazebo,它的一个显著优点是更加轻量级,并且是完全开源的。
## 1.2 ROS的概念和优势
ROS(Robot Operating System)并不是一个完整的操作系统,而是一个为机器人应用开发提供的软件框架和工具集。ROS使用图来表示计算过程,其中节点是基本的计算单位,可以通过话题(topics)、服务(services)以及动作(actions)等机制进行通信。ROS以其模块化、可复用和跨平台的特性,成为机器人开发领域的首选工具。它在学术界和工业界都有广泛应用,尤其在需要灵活的软件架构以支持复杂和多样的机器人应用开发时。
## 1.3 Pybullet与ROS的融合前景
将Pybullet与ROS结合起来,可以发挥两者的优势:Pybullet提供的高效仿真环境和ROS强大的生态系统及社区支持。这种结合使得机器人研发人员能够在开发阶段快速测试和迭代算法,同时使用ROS的各种工具和程序包进行更复杂的集成和部署工作。随着机器人技术的快速发展,Pybullet与ROS的集成成为了提高开发效率、缩短研发周期的有效途径。
# 2. Pybullet仿真环境搭建
## 2.1 Pybullet的基础理论和应用
### 2.1.1 Pybullet简介
Pybullet 是一款轻量级的机器人仿真软件,它被设计为能够提供快速的物理仿真能力,适合于机器学习、游戏开发、机器人学和计算机图形学等领域。与之功能相似的仿真软件如 Gazebo 相比,Pybullet 有着较低的学习曲线,更容易上手。Pybullet 既可以独立工作,也可以与 ROS(Robot Operating System)等机器人操作系统集成使用,为开发者提供了一个灵活的仿真平台。
### 2.1.2 Pybullet的工作原理
Pybullet 通过使用 Bullet Physics SDK 来模拟真实世界中的物理交互。用户可以通过编程的方式定义机器人模型、环境模型以及交互条件等,并通过仿真引擎进行各种物理运算,以此模拟出复杂的动态行为。Pybullet 可以在多种操作系统上运行,支持 Python、C++ 等多种编程语言,它的 API 设计简洁直观,使得用户能快速实现物理模型的搭建、测试和验证。
### 2.2 Pybullet仿真环境的配置
#### 2.2.1 安装Pybullet与环境搭建
在开始使用 Pybullet 之前,需要在本地计算机上安装 Python 和 Pybullet。安装 Pybullet 可以通过 pip 包管理器来完成。以下是安装 Pybullet 的基本命令:
```bash
pip install pybullet
```
安装完成后,就可以开始配置 Pybullet 仿真环境了。首先,需要导入 Pybullet 库,然后选择相应的仿真模式。Pybullet 提供了三种仿真模式,包括离线仿真、连接到 Pybullet 服务器和连接到 Bullet Physics 服务器。
#### 2.2.2 创建基本仿真环境
创建一个基本的仿真环境,我们可以从添加一个平面开始。平面在仿真中作为一个静止的环境部分,用于模拟地面或桌面等。代码如下:
```python
import pybullet as p
# 连接到 Pybullet 仿真器
p.connect(p.GUI)
# 设置仿真环境的重力值
p.setGravity(0, 0, -10)
# 加载地面平面
planeId = p.loadURDF("plane.urdf")
```
通过这段代码,我们加载了 Pybullet 自带的平面模型,并将其设置为当前仿真环境的一部分。通常情况下,平面 URDF(Unified Robot Description Format)文件位于 Pybullet 的安装目录中,或者在 Pybullet 的示例文件夹中。
### 2.3 Pybullet中的机器人模型导入
#### 2.3.1 现有模型的导入方法
Pybullet 支持多种格式的模型导入,例如 URDF、SDF 和 MJCF。对于刚入门的用户,推荐使用 URDF 格式,它是一种描述机器人模型的标准格式,用于描述机器人的链接、关节、碰撞和视觉信息等。
导入现有 URDF 模型的基本代码如下:
```python
# 加载一个 URDF 格式的机器人模型
robotId = p.loadURDF("robot.urdf")
```
#### 2.3.2 自定义模型的创建与导入
除了使用现成的模型,Pybullet 也支持用户根据需求自定义模型。自定义模型需要用户根据自己的项目需求,从几何形状、物理属性、动力学参数等方面手动定义。使用 Pybullet 自带的 API 可以非常方便地创建简单的几何形状和物理体。
例如,创建一个简单的长方体作为自定义模型的示例:
```python
# 创建一个简单的长方体
boxId = p.createCollisionShape(p.GEOM_BOX, halfExtents=[1, 0.5, 0.25])
# 在仿真环境中添加这个几何形状
p.createMultiBody(1, boxId, -1, [0, 0, 0.5])
```
上述代码中,`createCollisionShape` 函数创建了一个长方体碰撞形状,`createMultiBody` 函数则在仿真世界中添加了这个几何形状,并使其在指定位置悬空。
Pybullet 的仿真环境搭建是一个循序渐进的过程,通过逐步了解和应用基础理论,可以逐步深入到更高级的应用,例如集成到 ROS 等机器人操作系统中,实现更加复杂的仿真和控制任务。
# 3. ROS机器人控制基础
## 3.1 ROS的系统架构和工作原理
### 3.1.1 ROS的核心概念
ROS(Robot Operating System)并不是一个真正的操作系统,而是一个用于编写机器人软件程序的元操作系统。它提供了操作系统所应有的一些底层功能,比如硬件抽象描述、底层设备控制、常用功能的实现、进程间消息传递等等。在ROS的架构中,节点(Node)是构成ROS程序的基础单元,它们可以执行具体的功能任务,并通过话题(Topic)、服务(Service)和行为(Action)等方式进行通信。
### 3.1.2 ROS的消息传递机制
消息传递是ROS中最为关键的功能之一,其主要通过以下三种方式实现:
- **话题(Topic):** 话题是一个基于发布/订阅模式的消息传递系统。一个节点可以发布消息到特定的话题上,其他节点可以订阅该话题来接收消息。这类似于广播系统,一个发布者可以和多个订阅者进行通信。
- **服务(Service):** 服务是一种同步通信机制。服务请求者(客户端)发送一个请求到服务提供者(服务器),服务提供者处理完请求后返回一个响应。服务通信需要请求和响应的数据结构事先定义好。
- **行为(Action):** 行为机制是针对长时间运行的任务设计的。客户端发送一个目标(Goal)给服务器,服务器执行任务并可以发送反馈以及最终结果。行为通信适用于复杂任务的控制,如导航和路径规划。
## 3.2 ROS中机器人的基本控制
### 3.2.1 ROS控制框架简介
ROS提供了一套用于机器人控制的框架,称为ROS控制(ros_control)。这个框架包括了硬件抽象层(HAL)、控制器管理器、资源管理器、关节状态发布器和硬件接口。通过ros_control框架,用户可以轻松地实现对机器人驱动的控制,包括位置控制、力矩控制和速度控制等。
### 3.2.2 使用ROS控制机器人运动
控制机器人的运动通常需要了解机器人的运动学。运动学分为正运动学和逆运动学两部分。正运动学是已知关节角度,计算机器人末端执行器的位置和姿态;逆运动学则是已知末端执行器的位置和姿态,计算应该设定的关节角度。
在ROS中,通过将机器人模型导入到MoveIt!这样的运动规划框架中,我们可以很容易地控制机器人的运动。MoveIt!提供了丰富的接口来规划机器人的路径,并可直接通过话题、服务或行动库与控制器通信来实现运动控制。
## 3.3 ROS消息与服务的实践
### 3.3.1 ROS消息的使用方法
ROS消息是传递信息的基本数据结构,通常定义在`.msg`文件中。下面是一个简单的消息定义和代码示例:
```plaintext
# 定义一个简单的消息
string message
```
发布消息到一个话题的代码片段:
```python
#!/usr/bin/env python
import rospy
from std_msgs.msg import String
def talker():
pub = rospy.Publisher('chatter', String, queue_size=10)
rospy.init_node('talker', anonymous=True)
rate = rospy.Rate(10) # 10hz
while not rospy.is_shutdown():
hello_str = "hello world %s" % rospy.get_time()
rospy.loginfo(hello_str)
pub.publish(hello_str)
rate.sleep()
if __name__ == '__main__':
try:
talker()
except rospy.ROSInterruptException:
pass
```
### 3.3.2 ROS服务的创建与调用
服务提供了一种请求/响应机制,适用于不需要持续通信的场景。服务由服务端(SrvServer)和客户端(SrvClient)构成,他们共享相同的服务定义。
下面是一个简单服务定义和服务端的代码示例:
```plaintext
# service定义
string data
string result
```
服务端代码:
```python
#!/usr/bin/env python
import rospy
from your_package.srv import AddTwoInts, AddTwoIntsResponse
def handle_add_two_ints(req):
print("Returning [%s + %s = %s]"%(req.a, req.b, (req.a + req.b)))
return AddTwoIntsResponse(req.a + req.b)
def add_two_ints_server():
rospy.init_node('add_two_ints_server')
s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints)
print("Ready to add two ints.")
rospy.spin()
if __name__ == '__main__':
add_two_ints_server()
```
通过定义服务消息和创建服务端,我们可以实现对服务请求的处理。客户端可以调用这个服务,并接收处理结果。
# 4. Pybullet与ROS的集成与应用
## 4.1 Pybullet与ROS的集成方法
### 4.1.1 集成流程概述
在机器人仿真和开发中,集成Pybullet和ROS是一个重要的步骤,它能够将两者的优势结合在一起。Pybullet提供了强大的物理仿真能力,而ROS提供了丰富的机器人开发工具和社区支持。集成流程通常遵循以下步骤:
1. **环境准备:**确保你的系统中已经安装了ROS和Pybullet。如果没有,需要根据官方指南进行安装。
2. **通信机制:**确定Pybullet和ROS之间的通信机制。一般采用ROS的Topic、Service或Action作为通信接口。
3. **数据格式转换:**由于Pybullet和ROS可能使用不同的数据格式和单位系统,因此需要开发相应的转换器。
4. **集成脚本编写:**编写脚本或程序来实现ROS和Pybullet之间的交互和数据传递。
5. **仿真测试:**通过一个简单的仿真项目测试整个集成系统是否运行稳定。
### 4.1.2 集成过程中的关键步骤
在集成过程中,有一些关键步骤需要特别注意,以确保最终的效果符合预期:
- **精确的数据同步:**在仿真过程中,Pybullet和ROS需要实时交换数据,如机器人的状态和外部环境的变化,数据同步的精确度直接影响仿真结果的可信度。
- **模块化设计:**将集成过程中的各个模块设计成高度模块化的,以便于调试和后期维护。
- **错误处理机制:**集成系统应具备有效的错误检测和处理机制,当出现问题时能够迅速定位并处理。
- **性能优化:**仿真系统可能会消耗大量计算资源,因此需要对系统进行性能优化,以保证仿真效率。
## 4.2 Pybullet在ROS中的仿真应用
### 4.2.1 在ROS中使用Pybullet进行仿真
在ROS中使用Pybullet进行仿真首先需要在ROS的package中加入Pybullet的支持。一个简单的集成示例如下:
1. 创建ROS包,假设命名为 `pybullet_ros`。
2. 在该ROS包中添加Pybullet依赖。
```bash
cd ~/catkin_ws/src/pybullet_ros
catkin_create_pkg . std_msgs rospy roscpp
cd ~/catkin_ws
catkin_make
```
3. 创建一个Python脚本文件,例如`simple_simulator.py`,并引入Pybullet和ROS库。
```python
#!/usr/bin/env python
import rospy
import pybullet as p
import time
def simple_pybullet_simulation():
rospy.init_node('pybullet_simulator', anonymous=True)
# 创建仿真环境
p.connect(p.GUI)
p.setGravity(0,0,-10)
# 这里可以添加更多的仿真设置
# ...
p.disconnect()
if __name__ == '__main__':
try:
simple_pybullet_simulation()
except rospy.ROSInterruptException:
pass
```
4. 运行你的仿真脚本。
```bash
rosrun pybullet_ros simple_pybullet_simulation.py
```
在上述代码中,我们初始化了ROS节点,并使用Pybullet创建了一个简单的仿真环境。需要注意的是,所有的Pybullet仿真都需要在ROS节点运行的前提下进行。
### 4.2.2 仿真实验的实例演示
为了演示如何使用Pybullet与ROS进行更复杂的仿真实验,让我们来看一个移动机器人在Pybullet环境中进行路径规划的案例。
1. **建立机器人模型:**首先在Pybullet中导入或创建一个机器人模型。
2. **添加环境因素:**在仿真世界中添加障碍物或其他需要考虑的环境因素。
3. **编写控制脚本:**编写脚本来控制机器人移动,并实现路径规划算法。
4. **集成ROS话题:**将控制脚本与ROS话题连接,这样我们可以通过ROS话题发送控制命令,比如新的目标位置。
5. **启动仿真:**启动整个仿真系统并观察机器人的行为。
一个控制脚本的示例如下:
```python
# 假设这是在ROS的一个节点中运行的部分代码
def move_robot(target_position):
# 这里省略了路径规划和发送控制命令的细节
# ...
p.setJointMotorControlArray(robot_id, joint_indices, p.POSITION_CONTROL, target_positions)
p.stepSimulation()
# ...
def path_planning_callback(msg):
# msg 是ROS消息,包含了目标位置
move_robot(msg.position)
rospy.Subscriber('/path_planning', PoseStamped, path_planning_callback)
rospy.spin()
```
## 4.3 Pybullet与ROS的控制结合
### 4.3.1 从仿真到实际控制的转换
当我们完成了在Pybullet上的仿真测试,并确认控制策略有效时,可以将这一策略部署到真实机器人上。这个过程涉及以下关键步骤:
- **策略验证:**首先需要在真实机器人上进行策略的验证,确保仿真策略的可靠性和可行性。
- **设备接口适配:**适配ROS控制节点与机器人硬件接口,包括电机驱动器、传感器等。
- **参数微调:**将仿真中的参数根据真实世界的反馈进行微调。
- **系统测试:**在真实环境下进行全面测试,以确保控制策略在真实世界中的表现符合预期。
### 4.3.2 控制策略的测试与优化
测试和优化控制策略是确保机器人表现的关键步骤。下面是一些常用的测试和优化方法:
- **性能测试:**通过一系列标准化测试评估机器人的性能,包括响应时间、精确度和稳定性。
- **故障分析:**在测试过程中收集和分析故障数据,以改进控制策略。
- **模拟场景测试:**构建与现实场景相似的测试环境,确保机器人能够适应各种复杂条件。
- **用户反馈:**通过用户反馈来进一步调整和优化控制策略。
在这一章节,我们介绍了Pybullet与ROS集成的关键步骤和方法,深入到仿真实验的实例演示,以及如何将仿真策略转化为真实机器人的控制策略,并进行了测试与优化。这些内容为读者提供了一套完整的从仿真到真实世界控制流程的方法论。
# 5. Pybullet+ROS在机器人技术中的创新应用
## 5.1 自主导航与路径规划的实现
### 5.1.1 算法介绍与集成
在自主导航与路径规划这一创新应用中,算法的选择是至关重要的。一个典型的算法是基于图搜索的A*算法,它结合了最优搜索和启发式搜索的优点,能有效地找到从起点到终点的最短路径。此外,随着深度学习的发展,基于深度强化学习的路径规划方法也逐渐成为研究热点,这种方法通过与环境的交互学习最优策略,具有很好的适应性和泛化能力。
集成这些算法到Pybullet与ROS的框架中需要一些关键步骤。首先,在ROS环境中,需要创建或扩展一个现有的节点来处理路径规划。这个节点需要能够接收传感器数据、构建环境地图,并执行规划算法。路径规划完成后,节点生成的路径将被转化为ROS控制命令,通过Pybullet模拟器中控制机器人模型沿着规划的路径移动。
代码块示例:
```python
# 以下是一个简化的路径规划节点的伪代码示例
import rospy
from geometry_msgs.msg import PoseStamped
from nav_msgs.msg import Path
# 初始化ROS节点
rospy.init_node('path_planning_node')
# 定义路径发布者
path_pub = rospy.Publisher('path', Path, queue_size=10)
# 主循环
while not rospy.is_shutdown():
# 假设已经规划好了路径path_msg
path_msg = Path()
# 填充路径点信息
for pose in planned_path:
poseStamped = PoseStamped()
poseStamped.header.frame_id = 'map'
poseStamped.pose = pose
path_msg.poses.append(poseStamped)
# 发布路径信息
path_pub.publish(path_msg)
```
### 5.1.2 实际应用案例分析
以一个仓库机器人的实际应用为例,该机器人需要在仓库中自主导航以完成货物的搬运任务。应用中,首先使用ROS构建了仓库的环境地图,并利用激光雷达数据进行实时更新。然后,基于A*算法设计的路径规划节点在ROS中被集成和调用,实现了从起点到终点的高效路径规划。
在此过程中,机器人在Pybullet仿真环境中模拟运行,确保了算法的有效性和可行性。该案例不仅验证了路径规划算法的效率,而且表明了Pybullet与ROS集成在机器人技术应用中的巨大潜力。
## 5.2 复杂环境下的机器人操作
### 5.2.1 复杂环境建模
在复杂环境下进行机器人操作时,环境模型的构建至关重要。环境模型需要包含障碍物、空间布局、动态元素等信息。使用Pybullet进行复杂环境建模时,可以通过创建障碍物对象、设置障碍物的物理属性以及导入详细的环境地图来实现。
通过ROS提供的传感器数据,可以对环境进行动态更新,以反映现实世界中的变化,如物体的移动或新障碍物的出现。建模完成后,可以在Pybullet仿真环境中模拟机器人在该环境中的操作,如避障、路径规划和抓取物体等。
### 5.2.2 操作策略与实践
在复杂环境中,机器人的操作策略需要足够灵活和鲁棒。以动态避障为例,可以采用一种基于行为的控制策略,如DWA(动态窗口法)。DWA是一种在复杂动态环境中有效地控制移动机器人路径和速度的方法,它考虑了机器人的动力学约束,并在每一步中选择最佳的速度向量。
在ROS中实现DWA算法需要创建一个新的节点,该节点订阅机器人的速度和位置信息,并发布控制命令到机器人。通过集成Pybullet和ROS,我们可以测试这个策略,并观察机器人在仿真环境中的实时表现,以此来优化策略参数。
## 5.3 未来发展方向与展望
### 5.3.1 技术趋势分析
随着机器人技术的不断进步,Pybullet与ROS的集成将发挥更加重要的作用。一方面,Pybullet仿真平台在物理模型的精度和计算效率方面不断优化,将更好地模拟现实世界的复杂性。另一方面,ROS框架不断扩展新的功能和接口,提高了机器人软件系统的模块化和可复用性。
未来的发展趋势还包括云计算和边缘计算的融合,这意味着机器人不仅能在本地环境中处理复杂任务,还可以利用云资源进行大规模数据处理和深度学习模型的训练。此外,人工智能技术的发展将进一步提升机器人的自主决策能力,从而在更多复杂和动态的环境中实现高效的作业。
### 5.3.2 潜在应用场景探讨
在潜在的应用场景方面,Pybullet与ROS的集成有望在多个领域发挥重要作用。例如,在工业制造中,使用这种集成技术可以提高自动化水平,降低人工成本。在探索与救援任务中,通过模拟复杂的未知环境,可以为实际操作提供有效的训练和支持。
此外,家庭服务机器人、医疗护理机器人以及自动驾驶车辆等领域都将是Pybullet与ROS集成技术的潜在应用场景。这些场景中的机器人需要处理各种复杂的任务和环境,利用Pybullet与ROS的集成技术,可以大幅提高其智能化和自主性水平。
# 6. Pybullet+ROS实践项目案例
## 6.1 项目案例的选择与设计
选择合适的项目案例对于成功实践Pybullet和ROS至关重要。一个恰当的案例不仅能够帮助我们理解技术的集成,还能展示这些工具在现实世界问题解决中的应用。
### 6.1.1 案例项目的选择标准
一个理想的案例项目需要满足以下标准:
- **可学习性**:案例应涵盖Pybullet和ROS的主要功能和集成点。
- **挑战性**:项目应具有一定复杂性,挑战技术极限。
- **实用价值**:解决的问题应具有实际应用背景或能够启发新思路。
- **可扩展性**:案例应设计有扩展空间,允许后续深入研究或优化。
### 6.1.2 项目需求分析与设计
在选择案例后,接下来是进行需求分析与设计阶段。例如,一个案例是开发一个自动化巡检机器人,它需要在设定路径上自主导航,并进行视频流分析检测异常。
#### 项目需求
- **自主导航能力**:机器人需在室内环境中基于预先设定的地图进行路径规划。
- **视觉识别能力**:机器人需具备视觉识别能力,能够识别并报告预设目标或异常情况。
- **远程控制功能**:机器人可由远程操作人员进行干预控制。
#### 设计思路
- **机器人模型与仿真环境**:利用Pybullet设计机器人模型并创建与之对应的仿真环境。
- **控制策略实现**:通过ROS实现机器人的移动控制和视觉处理模块。
- **集成与测试**:将Pybullet中的仿真模型和ROS中的控制程序集成,进行系统级测试。
## 6.2 实际操作过程中的问题与解决
### 6.2.1 遇到的常见问题
在实际操作过程中,我们可能会遇到以下常见问题:
- **仿真与实际差异**:仿真环境和实际物理环境存在差异,导致控制策略在实际中效果不佳。
- **系统集成问题**:Pybullet仿真模型与ROS控制程序之间的集成不顺利,导致信息交换出现延迟或错误。
- **性能瓶颈**:系统在某些环节可能存在性能瓶颈,如处理速度不足以支持实时处理任务。
### 6.2.2 问题的解决策略与方法
对于上述问题,我们采取以下策略进行解决:
- **参数调整与优化**:通过调整仿真环境参数来减少与实际环境的差异。
- **代码审查与重构**:对系统集成点进行代码审查,优化接口设计,确保高效准确地交换信息。
- **性能测试与优化**:进行性能测试以识别瓶颈,实施针对性优化,比如使用更高效的算法或提升硬件性能。
## 6.3 项目案例的总结与反思
### 6.3.1 项目的成果总结
本项目成功构建了一个自动化巡检机器人原型,并在Pybullet仿真环境中进行了验证。通过与ROS集成,实现了基本的导航和视觉处理功能。测试结果表明,系统能在一定程度上满足预期目标。
### 6.3.2 对未来实践的思考与建议
尽管项目取得了初步成功,但还有很多工作可以进一步完善:
- **深度集成**:考虑如何进一步深化Pybullet与ROS之间的集成,以实现更复杂的交互和功能。
- **应用领域拓展**:探索该技术组合在其他领域的应用可能性,如农业机器人、救灾机器人等。
- **技术创新**:鼓励社区贡献新的算法或工具,以实现技术的持续创新和发展。
在未来的实践中,我们将根据项目案例的反馈和实际需求,持续优化和扩展Pybullet与ROS的集成应用。
0
0