Python 开发中有哪些高级技巧

Python015

Python 开发中有哪些高级技巧,第1张

技巧没有大用, 关系是思想与概念。 学得越多,技巧忘记得越多, 简单的深刻的道理则会陪伴你一生。

我自己在python中,如果说技巧,就是教程上说的,list的那些用法, dict, defaultdict, collection, set, array, numpy, blist, event, socket, cython, __init__, __all__, __doc__, keyerror还有些常用的库。 这些标准教程上的东西,你学会了,给自己帮助很大。 也都是技巧 。

偶尔用一用lambda, map, filter, zip就足够了。 可以缩小代码量。

多用multiprocess少用thread和threading。 有时间可以研究一下stackless python, twist,它的思想很受启发。 tornado, django, jinja2等都需要学一下,简单实用,强大。

最近流行的openstack也要看一下。zope这东西太古老了,如果你真有时间还是可以借鉴一下。

python2, python3都要学习。 不能一味抵制python3, 其中有很多好的思想。

GIL不是不可逾越的。

如果喜欢windows就学一学win32 api, 反之QT, wxwindows, gtk都可以看一看。 html5, node.js, javascript, bootstrap都是好的GUI工具。 要想快速开发, 很失望的说,只有在windows平台下可以找到可视化的快速开发工具。 C#, delphi, 都是很难超越的东西。

python用得人多, 用好的人也多。 但是水准高,思想又好,编程也强大,可以创造性的做产品的人不多。 相反,模仿别人, 借鉴其它的库,拿来主义,这是python的特点。 swig这个东西学一学。

以后你还会依赖其它的语言,需要的时候就多学一学其它的语言,甚至 ruby也是必要的。 java, c++, haskell, go, lua, javascript, php, scala.

但是有一天,python一定会衍生出不一样的版本。越超所有的语言,我相信。 但是不是最近。 这些所有的语言都没有挑战性。新的语言会带来新的设计模式。

消息类型:

1. Twist - 线速度角速度

通常被用于发送到/cmd_vel话题,被base controller节点监听,控制机器人运动

geometry_msgs/Twist

geometry_msgs/Vector3 linear

float64 x

float64 y

float64 z

geometry_msgs/Vector3 angular

float64 x

float64 y

float64 z

linear.x指向机器人前方,linear.y指向左方,linear.z垂直向上满足右手系,平面移动机器人常常linear.y和linear.z均为0

angular.z代表平面机器人的角速度,因为此时z轴为旋转轴

示例:

#! /usr/bin/env python'''Author: xushangnjlh at gmail dot com

Date: 2017/05/22

@package forward_and_back'''import rospyfrom geometry_msgs.msg import Twistfrom math import piclass ForwardAndBack():  def __init__(self):

rospy.init_node('forward_and_back', anonymous=False)

rospy.on_shutdown(self.shutdown)    # this "forward_and_back" node will publish Twist type msgs to /cmd_vel

# topic, where this node act like a Publisher

self.cmd_vel = rospy.Publisher('/cmd_vel', Twist, queue_size=5)

# parameters

rate = 50

r = rospy.Rate(rate)

linear_speed = 0.2

goal_distance = 5

linear_duration = goal_distance/linear_speed

angular_speed = 1.0

goal_angular = pi

angular_duration = goal_angular/angular_speed

# forward->rotate->back->rotate

for i in range(2):      # this is the msgs variant, has Twist type, no data now

move_cmd = Twist()

move_cmd.linear.x = linear_speed #

# should keep publishing

ticks = int(linear_duration*rate)      for t in range(ticks):      # one node can publish msgs to different topics, here only publish

# to /cmd_vel        self.cmd_vel.publish(move_cmd)

r.sleep # sleep according to the rate

# stop 1 ms before ratate

move_cmd = Twist()

self.cmd_vel.publish(move_cmd)

rospy.sleep(1)

move_cmd.angular_speed.z = angular_speed

ticks = int(angular_duration*rate)      for t in range(ticks):

self.cmd_vel.publish(move_cmd)

r.sleep()

self.cmd_vel.publish(Twist())

rospy.sleep(1)

def shutdown(self):

rospy.loginfo("Stopping the robot")

self.cmd_vel.publish(Twist())

rospy.sleep(1)    

if __name__=='__main__':  try:

ForwardAndBack()  except:

rospy.loginfo("forward_and_back node terminated by exception")

2. nav_msgs/Odometry - 里程计(位姿+线速度角速度+各自协方差)

通常,发布到/cmd_vel topic然后被机器人(例如/base_controller节点)监听到的Twist消息是不可能准确地转换为实际的机器人运动路径的,误差来源于机器人内部传感器误差(编码器),标定精度,以及环境影响(地面是否打滑平整);因此我们可以用更准确的Odometry消息类型类获取机器人位姿(/odom到/base_link的tf变换)。在标定准确时,该消息类型与真实的机器人位姿误差大致在1%量级(即使在rviz中显示的依然误差较大)。

还包括

参考系信息,Odometry使用/odom作为parent frame id,/base_link作为child frame id;也就是说世界参考系为/odom(通常设定为起始位姿,固定不动),移动参考系为/base_link(这里还有点不理解,后面来填坑)

时间戳,因此不仅知道运动轨迹,还可以知道对应时间点

Header header

string child_frame_id

geometry_msgs/PoseWithCovariance pose

geometry_msgs/TwistWithCovariance twist

# 展开Header header

uint32 seq

time stamp

string frame_id

string child_frame_id

geometry_msgs/PoseWithCovariance pose

geometry_msgs/Pose pose

geometry_msgs/Point position

float64 x

float64 y

float64 z

geometry_msgs/Quaternion orientation

float64 x

float64 y

float64 z

float64 w

float64[36] covariance

geometry_msgs/TwistWithCovariance twist

geometry_msgs/Twist twist

geometry_msgs/Vector3 linear

float64 x

float64 y

float64 z

geometry_msgs/Vector3 angular

float64 x

float64 y

float64 z

float64[36] covariance

示例:

运动路径和位姿通过内部的Odometry获取,该Odemetry的位姿通过监听tf坐标系变换获取(/odom和/base_link)

#! /usr/bin/env python'''Author: xushangnjlh at gmail dot com

Date: 2017/05/23

@package odometry_forward_and_back'''import rospyfrom geometry_msgs.msg import Twist, Point, Quaternionimport tffrom rbx1_nav.tranform_utils import quat_to_angle, normalize_anglefrom math import pi, radians, copysign, sqrt, powclass Odometry_forward_and_back:  def __init__(self):

   rospy.ini_node('odometry_forward_and_back', anonymous=False)

   rospy.on_shutdown(self.shutdown)

     

   self.cmd_vel = rospy.Publisher('/cmd_vel', Twist, queue_size=5)

     

   rate = 20

   r = rospy.Rate(rate)

   linear_speed = 0.2

   goal_distance =1.0

   angular_speed = 1.0

   goal_angle = pi

   angular_tolerance = radians(2.5)    

   # Initialize tf listener, and give some time to fill its buffer

   self.tf_listener = tf.TransformListener()

   rospy.sleep(2)    

   # Set odom_frame and base_frame

   self.odom_frame = '/odom'

   

   try:

     self.tf_listener.waitForTransform(self.odom_frame,

                                       '/base_footprint',

                                       rospy.Time(),

                                       rospy.Duration(1.0))

     self.base_frame = '/base_footprint'

   except(tf.Exception, tf.ConnectivityException, tf.LookupException):      try:

       self.tf_listener.waitForTransform(self.odom_frame,

                                         '/base_link',

                                         rospy.Time(),

                                         rospy.Duration(1.0))

       self.base_frame = '/base_link'

     except(tf.Exception, tf.ConnectivityException, tf.LookupException):

       rospy.loginfo("Cannot find base_frame transformed from /odom")

       rospy.signal_shutdown("tf Exception")

   

   position = Point()    

   for i in range(2):

     move_cmd = Twist()

     move_cmd.linear.x = linear_speed      # Initial pose, obtained from internal odometry

     (position, rotation) = self.get_odom()

     x_start = position.x

     y_start = position.y

     distance = 0      

     # Keep publishing Twist msgs, until the internal odometry reach the goal

     while distance <goal_distance and not rospy.is_shutdown():

       self.cmd_vel.publish(move_cmd)

       r.sleep()

       (position, rotation) = self.get_odom()

       distance = sqrt( pow( (position.x-x_start), 2 ) + \

         pow( (position.y-y_start), 2 ) )      

     # Stop 1 ms before rotate

     move_cmd = Twist()

     self.cmd_vel.publish(move_cmd)

     rospy.sleep(1)

     

     move_cmd.angular.z = angular_speed      # should be the current ration from odom

     angle_last = rotation

     angle_turn = 0      while abs(angle_turn+angular_tolerance) <abs(goal_angle) \        and not rospy.is_shutdown():

       self.cmd_vel.publish(move_cmd)

       r.sleep()

       (position, rotation) = self.get_odom

       delta_angle = normalize_angle(rotation - angle_last)

       angle_turn += delta_angle

       angle_last = rotation

     

     move_cmd = Twist()

     self.cmd_vel.publish(move_cmd)

     rospy.sleep(1)

     

     self.cmd_vel.publish(Twist())  

 def get_dom(self):    try:

     (trans, rot) = self.tf_listener.lookupTransfrom(self.odom_frame,

                                                     self.base_frame,

                                                     rospy.Time(0))    except(tf.Exception, tf.ConnectivityException, tf.LookupException):

     rospy.loginfo("TF exception, cannot get_dom()!")      return

   # angle is only yaw : RPY()[2]

   return (Point(*trans), quat_to_angle(*rot))  

 def shutdown(self):

   rospy.loginfo("Stopping the robot...")

   self.cmd_vel.publish(Twist(0))

   rospy.sleep(1)    

if __name__=='__main__':  try:

   Odometry_forward_and_back()  except:

   rospy.loginfo("Odometry_forward_and_back node terminated!")

注意这里存在tf操作:

self.tf_listener = tf.TransformListener()

rospy.sleep(2)

创建TransformListener对象监听坐标系变换,这里需要sleep 2ms用于tf缓冲。

可以通过以下API获取tf变换,保存在TransformListener对象中,通过lookupTransform获取:

# TransformListener.waitForTransform('ref_coordinate', 'moving_coordinate', rospy.Time(), rospy.Duration(1.0))self.tf_listener.waitForTransform(self.odom_frame, '/base_footprint', rospy.Time(), rospy.Duration(1.0))

(trans, rot) = self.tf_listener.lookupTransform(self.odom_frame, self.base_frame, rospy.Time(0))

你写了那么多, 其实就是

# dict为内建函数, 不建议作为变量名

def f(d):

    return max(d.values())

这里返回的只是年份的最大值